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 { logger } from '../../../logger';
import { ExternalHostError } from '../../../types/error';
import { Http, HttpOptions } from '../../../util/http';
import { Preset, PresetConfig } from '../common';
import { PRESET_DEP_NOT_FOUND, fetchPreset } from '../util';
@ -27,7 +27,7 @@ export async function fetchJSONFile(
res = await http.getJson(url, opts);
} catch (err) {
// istanbul ignore if: not testable with nock
if (err.message === PLATFORM_FAILURE) {
if (err instanceof ExternalHostError) {
throw err;
}
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 [
Object {
"headers": Object {

View file

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

View file

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

View file

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

View file

@ -5,7 +5,6 @@ export const SYSTEM_INSUFFICIENT_MEMORY = 'out-of-memory';
// Platform Error
export const PLATFORM_AUTHENTICATION_ERROR = 'authentication-error';
export const PLATFORM_BAD_CREDENTIALS = 'bad-credentials';
export const PLATFORM_FAILURE = 'platform-failure';
export const PLATFORM_GPG_FAILED = 'gpg-failed';
export const PLATFORM_INTEGRATION_UNAUTHORIZED = 'integration-unauthorized';
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_NO_PACKAGE_FILES = 'no-package-files';
// Datasource error
export const DATASOURCE_FAILURE = 'registry-failure';
// Host error
export const EXTERNAL_HOST_ERROR = 'external-host-error';
// Worker Error
export const WORKER_FILE_UPDATE_FAILED = 'update-failure';

View file

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

View file

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

View file

@ -1,5 +1,3 @@
import { DATASOURCE_FAILURE } from '../constants/error-messages';
export interface Config {
datasource?: string;
depName?: string;
@ -80,18 +78,3 @@ export interface Datasource {
defaultConfig?: object;
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`] = `
Array [

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ import fs from 'fs';
import { resolve } from 'path';
import nock from 'nock';
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 mavenVersioning from '../../versioning/maven';
import { id as datasource } from '.';
@ -182,7 +182,7 @@ describe('datasource/maven', () => {
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')
.get('/maven2/org/artifact/maven-metadata.xml')
.times(4)
@ -195,7 +195,7 @@ describe('datasource/maven', () => {
depName: 'org:artifact',
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 () => {

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@ import nock from 'nock';
import _registryAuthToken from 'registry-auth-token';
import { getPkgReleases } from '..';
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 { id as datasource, getNpmrc, resetCache, setNpmrc } from '.';
@ -204,13 +204,13 @@ describe(getName(__filename), () => {
nock('https://registry.npmjs.org').get('/foobar').reply(503);
await expect(
getPkgReleases({ datasource, depName: 'foobar' })
).rejects.toThrow(Error(DATASOURCE_FAILURE));
).rejects.toThrow(EXTERNAL_HOST_ERROR);
});
it('should throw error for 408', async () => {
nock('https://registry.npmjs.org').get('/foobar').reply(408);
await expect(
getPkgReleases({ datasource, depName: 'foobar' })
).rejects.toThrow(Error(DATASOURCE_FAILURE));
).rejects.toThrow(EXTERNAL_HOST_ERROR);
});
it('should throw error for others', async () => {
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 { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as globalCache from '../../util/cache/global';
import * as runCache from '../../util/cache/run';
import * as hostRules from '../../util/host-rules';
import { Http, HttpOptions } from '../../util/http';
import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common';
import { GetReleasesConfig, ReleaseResult } from '../common';
export const id = 'packagist';
export const defaultRegistryUrls = ['https://packagist.org'];
@ -313,10 +314,10 @@ async function packageLookup(
}
if (err.host === 'packagist.org') {
if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT') {
throw new DatasourceError(err);
throw new ExternalHostError(err);
}
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');

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { Http } from '../../util/http';
import { DatasourceError, ReleaseResult } from '../common';
import { ReleaseResult } from '../common';
import { id } from './common';
const http = new Http(id);
@ -38,7 +39,7 @@ async function updateRubyGemsVersions(): Promise<void> {
if (err.statusCode !== 416) {
contentLength = 0;
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')
);
}

View file

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

View file

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

View file

@ -3,9 +3,10 @@ import fs from 'fs-extra';
import upath from 'upath';
// eslint-disable-next-line import/no-unresolved
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 { platform } from '../../../platform';
import { ExternalHostError } from '../../../types/error';
import { getChildProcessEnv } from '../../../util/exec/env';
import { deleteLocalFile } from '../../../util/fs';
import * as hostRules from '../../../util/host-rules';
@ -432,7 +433,7 @@ export async function getAdditionalFiles(
const err = new Error(
'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' },
'lock file failed for the dependency being updated - skipping branch creation'
);
throw new DatasourceError(
throw new ExternalHostError(
new Error(
'lock file failed for the dependency being updated - skipping branch creation'
)
),
npmId
);
}
/* eslint-enable no-useless-escape */
@ -594,10 +596,11 @@ export async function getAdditionalFiles(
{ dependency: upgrade.depName, type: 'pnpm' },
'lock file failed for the dependency being updated - skipping branch creation'
);
throw new DatasourceError(
throw new ExternalHostError(
Error(
'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' },
'lock file failed for the dependency being updated - skipping branch creation'
);
throw new DatasourceError(
throw new ExternalHostError(
Error(
'lock file failed for the dependency being updated - skipping branch creation'
)
),
npmId
);
}
/* eslint-enable no-useless-escape */
@ -688,10 +692,11 @@ export async function getAdditionalFiles(
{ dependency: upgrade.depName, type: 'npm' },
'lock file failed for the dependency being updated - skipping branch creation'
);
throw new DatasourceError(
throw new ExternalHostError(
Error(
'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 { join } from 'upath';
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 { ExternalHostError } from '../../../types/error';
import { ExecOptions, exec } from '../../../util/exec';
import { PostUpdateConfig, Upgrade } from '../../common';
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.npmjs.org')
) {
throw new DatasourceError(err);
throw new ExternalHostError(err, npmId);
}
}
return { error: true, stderr: err.stderr };

View file

@ -4,13 +4,13 @@ import fs from 'fs-extra';
import Git from 'simple-git/promise';
import {
CONFIG_VALIDATION,
PLATFORM_FAILURE,
REPOSITORY_CHANGED,
REPOSITORY_EMPTY,
REPOSITORY_TEMPORARY_ERROR,
SYSTEM_INSUFFICIENT_DISK_SPACE,
} from '../../constants/error-messages';
import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import * as limits from '../../workers/global/limits';
import { CommitFilesConfig } from '../common';
import { writePrivateKey } from './private-key';
@ -54,7 +54,7 @@ function checkForPlatformFailure(err: Error): void {
];
for (const errorStr of platformFailureStrings) {
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')) {
throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE);
}
throw new Error(PLATFORM_FAILURE);
throw new ExternalHostError(err, 'git');
}
const durationMs = Math.round(Date.now() - cloneStart);
logger.debug({ durationMs }, 'git clone completed');

View file

@ -3,7 +3,6 @@ import is from '@sindresorhus/is';
import delay from 'delay';
import { configFileNames } from '../../config/app-strings';
import {
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED,
REPOSITORY_ACCESS_FORBIDDEN,
REPOSITORY_ARCHIVED,
@ -24,6 +23,7 @@ import {
} from '../../constants/pull-requests';
import { logger } from '../../logger';
import { BranchStatus } from '../../types';
import { ExternalHostError } from '../../types/error';
import * as hostRules from '../../util/host-rules';
import * as githubHttp from '../../util/http/github';
import { sanitize } from '../../util/sanitize';
@ -397,7 +397,7 @@ export async function initRepo({
}
);
} catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) {
if (err instanceof ExternalHostError) {
throw err;
}
if (
@ -965,7 +965,7 @@ export async function getPrList(): Promise<Pr[]> {
);
} catch (err) /* istanbul ignore next */ {
logger.debug({ err }, 'getPrList err');
throw new Error('platform-failure');
throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
}
config.prList = res.body.map((pr) => ({
number: pr.number,
@ -1094,7 +1094,7 @@ export async function getBranchStatus(
logger.debug({ result: checkRunsRaw }, 'No check runs found');
}
} catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) {
if (err instanceof ExternalHostError) {
throw err;
}
if (
@ -1506,7 +1506,7 @@ async function getComments(issueNo: number): Promise<Comment[]> {
} catch (err) /* istanbul ignore next */ {
if (err.statusCode === 404) {
logger.debug('404 respose when retrieving comments');
throw new Error(PLATFORM_FAILURE);
throw new ExternalHostError(err, PLATFORM_TYPE_GITHUB);
}
throw err;
}
@ -1559,7 +1559,7 @@ export async function ensureComment({
}
return true;
} catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) {
if (err instanceof ExternalHostError) {
throw err;
}
if (err.body?.message?.includes('is locked')) {
@ -1690,7 +1690,7 @@ export async function updatePr(
);
logger.debug({ pr: prNo }, 'PR updated');
} catch (err) /* istanbul ignore next */ {
if (err.message === PLATFORM_FAILURE) {
if (err instanceof ExternalHostError) {
throw err;
}
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 { getName } from '../../../test/util';
import {
EXTERNAL_HOST_ERROR,
PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED,
@ -126,7 +126,7 @@ describe(getName(__filename), () => {
},
});
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', () => {
const codes = ['ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN'];
@ -137,7 +137,7 @@ describe(getName(__filename), () => {
code,
});
expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE);
expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
}
});
it('should throw platform failure for 500', () => {
@ -146,14 +146,14 @@ describe(getName(__filename), () => {
message: 'Internal Server Error',
});
expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE);
expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
});
it('should throw platform failure ParseError', () => {
const e = getError({
name: 'ParseError',
});
expect(e).toBeDefined();
expect(e.message).toEqual(PLATFORM_FAILURE);
expect(e.message).toEqual(EXTERNAL_HOST_ERROR);
});
it('should throw for unauthorized integration', () => {
const e = getError({
@ -200,7 +200,7 @@ describe(getName(__filename), () => {
};
const e = getError(gotErr);
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', () => {
const gotErr = {

View file

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

View file

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

View file

@ -4,11 +4,9 @@ import { DateTime } from 'luxon';
import minimatch from 'minimatch';
import { RenovateConfig } from '../../config';
import {
DATASOURCE_FAILURE,
MANAGER_LOCKFILE_ERROR,
PLATFORM_AUTHENTICATION_ERROR,
PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_CHANGED,
@ -24,6 +22,7 @@ import { logger } from '../../logger';
import { getAdditionalFiles } from '../../manager/npm/post-update';
import { platform } from '../../platform';
import { BranchStatus } from '../../types';
import { ExternalHostError } from '../../types/error';
import { emojify } from '../../util/emoji';
import { exec } from '../../util/exec';
import { readLocalFile, writeLocalFile } from '../../util/fs';
@ -541,10 +540,7 @@ export async function processBranch(
err.message.includes('fatal: Authentication failed')
) {
throw new Error(PLATFORM_AUTHENTICATION_ERROR);
} else if (
err.message !== DATASOURCE_FAILURE &&
err.message !== PLATFORM_FAILURE
) {
} else if (!(err instanceof ExternalHostError)) {
logger.error({ err }, `Error updating branch: ${err.message}`);
}
// 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 */ {
if (
[
PLATFORM_RATE_LIMIT_EXCEEDED,
PLATFORM_FAILURE,
REPOSITORY_CHANGED,
].includes(err.message)
err instanceof ExternalHostError ||
[PLATFORM_RATE_LIMIT_EXCEEDED, REPOSITORY_CHANGED].includes(err.message)
) {
logger.debug('Passing PR error up');
throw err;

View file

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

View file

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

View file

@ -2,12 +2,11 @@ import { RenovateConfig } from '../../config';
import {
CONFIG_VALIDATION,
DATASOURCE_FAILURE,
EXTERNAL_HOST_ERROR,
MANAGER_LOCKFILE_ERROR,
MANAGER_NO_PACKAGE_FILES,
PLATFORM_AUTHENTICATION_ERROR,
PLATFORM_BAD_CREDENTIALS,
PLATFORM_FAILURE,
PLATFORM_INTEGRATION_UNAUTHORIZED,
PLATFORM_RATE_LIMIT_EXCEEDED,
REPOSITORY_ACCESS_FORBIDDEN,
@ -28,8 +27,8 @@ import {
SYSTEM_INSUFFICIENT_MEMORY,
UNKNOWN_ERROR,
} from '../../constants/error-messages';
import { DatasourceError } from '../../datasource/common';
import { logger } from '../../logger';
import { ExternalHostError } from '../../types/error';
import { raiseConfigWarningIssue } from './error-config';
export default async function handleError(
@ -107,22 +106,12 @@ export default async function handleError(
await raiseConfigWarningIssue(config, err);
return err.message;
}
if (err instanceof DatasourceError) {
if (err instanceof ExternalHostError) {
logger.warn(
{ datasource: err.datasource, lookupName: err.lookupName, err: err.err },
'Datasource failure'
{ hostType: err.hostType, lookupName: err.lookupName, err: err.err },
'Host error'
);
logger.info('Registry error - 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');
logger.info('External host error causing abort - skipping');
delete config.branchList; // eslint-disable-line no-param-reassign
return err.message;
}
@ -174,7 +163,7 @@ export default async function handleError(
logger.warn({ err }, 'Git error - aborting');
delete config.branchList; // eslint-disable-line no-param-reassign
// rewrite this error
return PLATFORM_FAILURE;
return EXTERNAL_HOST_ERROR;
}
if (
err.message.includes('The remote end hung up unexpectedly') ||
@ -183,7 +172,7 @@ export default async function handleError(
logger.warn({ err }, 'Git error - aborting');
delete config.branchList; // eslint-disable-line no-param-reassign
// rewrite this error
return PLATFORM_FAILURE;
return EXTERNAL_HOST_ERROR;
}
// Swallow this error so that other repositories can be processed
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 { migrateAndValidate } from '../../../config/migrate-validate';
import * as presets from '../../../config/presets';
import {
CONFIG_VALIDATION,
PLATFORM_FAILURE,
} from '../../../constants/error-messages';
import { CONFIG_VALIDATION } from '../../../constants/error-messages';
import * as npmApi from '../../../datasource/npm';
import { logger } from '../../../logger';
import { platform } from '../../../platform';
import { ExternalHostError } from '../../../types/error';
import { readLocalFile } from '../../../util/fs';
import * as hostRules from '../../../util/host-rules';
import { flattenPackageRules } from './flatten';
@ -59,7 +57,10 @@ export async function mergeRenovateConfig(
// istanbul ignore if
if (renovateConfig === 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
if (!renovateConfig.length) {