mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-30 10:38:55 +00:00
Improve NotFoundError handling
Use isNotFoundError which converts an error of the default type `unknown` into NotFoundError if the error is an instance of NotFoundError. Thrown errors are of type `unknown` by default so we should use methods with type guards for better type narrowing. Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
parent
80e213ab7e
commit
75bf2aa3b4
@ -9,7 +9,7 @@ services:
|
|||||||
dockerfile: Dockerfile.template
|
dockerfile: Dockerfile.template
|
||||||
args:
|
args:
|
||||||
ARCH: ${ARCH:-amd64}
|
ARCH: ${ARCH:-amd64}
|
||||||
command: ['/wait-for-it.sh', '--', '/usr/src/app/entry.sh']
|
command: [ '/wait-for-it.sh', '--', '/usr/src/app/entry.sh' ]
|
||||||
# Use bridge networking for the tests
|
# Use bridge networking for the tests
|
||||||
network_mode: 'bridge'
|
network_mode: 'bridge'
|
||||||
networks:
|
networks:
|
||||||
@ -30,7 +30,7 @@ services:
|
|||||||
- ./test/data/root:/mnt/root
|
- ./test/data/root:/mnt/root
|
||||||
- ./test/lib/wait-for-it.sh:/wait-for-it.sh
|
- ./test/lib/wait-for-it.sh:/wait-for-it.sh
|
||||||
tmpfs:
|
tmpfs:
|
||||||
- /data
|
- /data # sqlite3 database
|
||||||
|
|
||||||
dbus:
|
dbus:
|
||||||
image: balenablocks/dbus
|
image: balenablocks/dbus
|
||||||
@ -74,7 +74,7 @@ services:
|
|||||||
'--',
|
'--',
|
||||||
'npm',
|
'npm',
|
||||||
'run',
|
'run',
|
||||||
'test:integration',
|
'test:integration'
|
||||||
]
|
]
|
||||||
depends_on:
|
depends_on:
|
||||||
- balena-supervisor
|
- balena-supervisor
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"test:env": "ARCH=$(./build-utils/detect-arch.sh) docker-compose -f docker-compose.test.yml -f docker-compose.dev.yml up --build; npm run compose:down",
|
"test:env": "ARCH=$(./build-utils/detect-arch.sh) docker-compose -f docker-compose.test.yml -f docker-compose.dev.yml up --build; npm run compose:down",
|
||||||
"test:compose": "ARCH=$(./build-utils/detect-arch.sh) docker-compose -f docker-compose.yml -f docker-compose.test.yml up --build --remove-orphans --exit-code-from=sut ; npm run compose:down",
|
"test:compose": "ARCH=$(./build-utils/detect-arch.sh) docker-compose -f docker-compose.yml -f docker-compose.test.yml up --build --remove-orphans --exit-code-from=sut ; npm run compose:down",
|
||||||
"test": "npm run lint && npm run test:build && npm run test:unit && npm run test:legacy",
|
"test": "npm run lint && npm run test:build && npm run test:unit && npm run test:legacy",
|
||||||
"compose:down": "docker-compose -f docker-compose.test.yml down",
|
"compose:down": "docker-compose -f docker-compose.test.yml down && docker volume rm $(docker volume ls -f name=balena-supervisor -q)",
|
||||||
"prettify": "balena-lint -e ts -e js --fix src/ test/ typings/ build-utils/ webpack.config.js",
|
"prettify": "balena-lint -e ts -e js --fix src/ test/ typings/ build-utils/ webpack.config.js",
|
||||||
"release": "tsc --project tsconfig.release.json && mv build/src/* build",
|
"release": "tsc --project tsconfig.release.json && mv build/src/* build",
|
||||||
"sync": "ts-node --files sync/sync.ts",
|
"sync": "ts-node --files sync/sync.ts",
|
||||||
|
@ -18,7 +18,7 @@ import constants = require('../lib/constants');
|
|||||||
|
|
||||||
import { getStepsFromStrategy } from './update-strategies';
|
import { getStepsFromStrategy } from './update-strategies';
|
||||||
|
|
||||||
import { InternalInconsistencyError, NotFoundError } from '../lib/errors';
|
import { InternalInconsistencyError, isNotFoundError } from '../lib/errors';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import { checkTruthy, checkString } from '../lib/validation';
|
import { checkTruthy, checkString } from '../lib/validation';
|
||||||
import { ServiceComposeConfig, DeviceMetadata } from './types/service';
|
import { ServiceComposeConfig, DeviceMetadata } from './types/service';
|
||||||
@ -804,8 +804,8 @@ export class App {
|
|||||||
let imageInfo: ImageInspectInfo | undefined;
|
let imageInfo: ImageInspectInfo | undefined;
|
||||||
try {
|
try {
|
||||||
imageInfo = await imageManager.inspectByName(svc.image);
|
imageInfo = await imageManager.inspectByName(svc.image);
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import { DeltaFetchOptions, FetchOptions, docker } from '../lib/docker-utils';
|
|||||||
import * as dockerUtils from '../lib/docker-utils';
|
import * as dockerUtils from '../lib/docker-utils';
|
||||||
import {
|
import {
|
||||||
DeltaStillProcessingError,
|
DeltaStillProcessingError,
|
||||||
NotFoundError,
|
isNotFoundError,
|
||||||
StatusError,
|
StatusError,
|
||||||
} from '../lib/errors';
|
} from '../lib/errors';
|
||||||
import * as LogTypes from '../lib/log-types';
|
import * as LogTypes from '../lib/log-types';
|
||||||
@ -236,8 +236,8 @@ export async function triggerFetch(
|
|||||||
await markAsSupervised({ ...image, dockerImageId: img.Id });
|
await markAsSupervised({ ...image, dockerImageId: img.Id });
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
if (!(e instanceof ImageDownloadBackoffError)) {
|
if (!(e instanceof ImageDownloadBackoffError)) {
|
||||||
addImageFailure(image.name);
|
addImageFailure(image.name);
|
||||||
}
|
}
|
||||||
@ -729,8 +729,8 @@ async function removeImageIfNotNeeded(image: Image): Promise<void> {
|
|||||||
|
|
||||||
// Mark the image as removed
|
// Mark the image as removed
|
||||||
removed = true;
|
removed = true;
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (NotFoundError(e)) {
|
if (isNotFoundError(e)) {
|
||||||
removed = false;
|
removed = false;
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -3,7 +3,7 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { docker } from '../lib/docker-utils';
|
import { docker } from '../lib/docker-utils';
|
||||||
import { NotFoundError } from '../lib/errors';
|
import { isNotFoundError } from '../lib/errors';
|
||||||
import logTypes = require('../lib/log-types');
|
import logTypes = require('../lib/log-types');
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
import { exists } from '../lib/fs-utils';
|
import { exists } from '../lib/fs-utils';
|
||||||
@ -45,8 +45,8 @@ export async function create(network: Network) {
|
|||||||
|
|
||||||
// We have a network with the same config and name
|
// We have a network with the same config and name
|
||||||
// already created, we can skip this
|
// already created, we can skip this
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
logger.logSystemEvent(logTypes.createNetworkError, {
|
logger.logSystemEvent(logTypes.createNetworkError, {
|
||||||
network: { name: network.name, appUuid: network.appUuid },
|
network: { name: network.name, appUuid: network.appUuid },
|
||||||
error: e,
|
error: e,
|
||||||
@ -120,7 +120,7 @@ export function ensureSupervisorNetwork(): Bluebird<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(NotFoundError, () => {
|
.catch(isNotFoundError, () => {
|
||||||
log.debug(`Creating ${constants.supervisorNetworkInterface} network`);
|
log.debug(`Creating ${constants.supervisorNetworkInterface} network`);
|
||||||
return Bluebird.resolve(
|
return Bluebird.resolve(
|
||||||
docker.createNetwork({
|
docker.createNetwork({
|
||||||
|
@ -15,7 +15,7 @@ import { PermissiveNumber } from '../config/types';
|
|||||||
import constants = require('../lib/constants');
|
import constants = require('../lib/constants');
|
||||||
import {
|
import {
|
||||||
InternalInconsistencyError,
|
InternalInconsistencyError,
|
||||||
NotFoundError,
|
isNotFoundError,
|
||||||
StatusCodeError,
|
StatusCodeError,
|
||||||
isStatusError,
|
isStatusError,
|
||||||
} from '../lib/errors';
|
} from '../lib/errors';
|
||||||
@ -72,8 +72,8 @@ export const getAll = async (
|
|||||||
service.status = vState.status;
|
service.status = vState.status;
|
||||||
}
|
}
|
||||||
return service;
|
return service;
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (NotFoundError(e)) {
|
if (isNotFoundError(e)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
@ -206,8 +206,8 @@ export async function remove(service: Service) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await docker.getContainer(existingService.containerId).remove({ v: true });
|
await docker.getContainer(existingService.containerId).remove({ v: true });
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
logger.logSystemEvent(LogTypes.removeDeadServiceError, {
|
logger.logSystemEvent(LogTypes.removeDeadServiceError, {
|
||||||
service,
|
service,
|
||||||
error: e,
|
error: e,
|
||||||
@ -227,8 +227,8 @@ async function create(service: Service) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return docker.getContainer(existing.containerId);
|
return docker.getContainer(existing.containerId);
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
logger.logSystemEvent(LogTypes.installServiceError, {
|
logger.logSystemEvent(LogTypes.installServiceError, {
|
||||||
service,
|
service,
|
||||||
error: e,
|
error: e,
|
||||||
@ -383,8 +383,8 @@ export function listenToEvents() {
|
|||||||
let service: Service | null = null;
|
let service: Service | null = null;
|
||||||
try {
|
try {
|
||||||
service = await getByDockerContainerId(data.id);
|
service = await getByDockerContainerId(data.id);
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import * as Path from 'path';
|
|||||||
import { VolumeInspectInfo } from 'dockerode';
|
import { VolumeInspectInfo } from 'dockerode';
|
||||||
|
|
||||||
import constants = require('../lib/constants');
|
import constants = require('../lib/constants');
|
||||||
import { NotFoundError, InternalInconsistencyError } from '../lib/errors';
|
import { isNotFoundError, InternalInconsistencyError } from '../lib/errors';
|
||||||
import { safeRename } from '../lib/fs-utils';
|
import { safeRename } from '../lib/fs-utils';
|
||||||
import { docker } from '../lib/docker-utils';
|
import { docker } from '../lib/docker-utils';
|
||||||
import * as LogTypes from '../lib/log-types';
|
import * as LogTypes from '../lib/log-types';
|
||||||
@ -58,8 +58,8 @@ export async function create(volume: Volume): Promise<void> {
|
|||||||
if (!volume.isEqualConfig(existing)) {
|
if (!volume.isEqualConfig(existing)) {
|
||||||
throw new ResourceRecreationAttemptError('volume', volume.name);
|
throw new ResourceRecreationAttemptError('volume', volume.name);
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (!NotFoundError(e)) {
|
if (!isNotFoundError(e)) {
|
||||||
logger.logSystemEvent(LogTypes.createVolumeError, {
|
logger.logSystemEvent(LogTypes.createVolumeError, {
|
||||||
volume: { name: volume.name },
|
volume: { name: volume.name },
|
||||||
error: e,
|
error: e,
|
||||||
|
@ -22,16 +22,23 @@ export class StatusError extends Error {
|
|||||||
export const isStatusError = (x: unknown): x is StatusError =>
|
export const isStatusError = (x: unknown): x is StatusError =>
|
||||||
x != null && x instanceof Error && !isNaN((x as any).statusCode);
|
x != null && x instanceof Error && !isNaN((x as any).statusCode);
|
||||||
|
|
||||||
|
export class NotFoundError extends Error {
|
||||||
|
public statusCode: number;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.statusCode = 404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isNotFoundError = (e: unknown): e is NotFoundError =>
|
||||||
|
isStatusError(e) && e.statusCode === 404;
|
||||||
|
|
||||||
interface CodedSysError extends Error {
|
interface CodedSysError extends Error {
|
||||||
code?: string;
|
code?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeviceNotFoundError extends TypedError {}
|
export class DeviceNotFoundError extends TypedError {}
|
||||||
|
|
||||||
export function NotFoundError(err: StatusCodeError): boolean {
|
|
||||||
return checkInt(err.statusCode) === 404;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ENOENT(err: CodedSysError): boolean {
|
export function ENOENT(err: CodedSysError): boolean {
|
||||||
return err.code === 'ENOENT';
|
return err.code === 'ENOENT';
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import * as applicationManager from '../compose/application-manager';
|
|||||||
import {
|
import {
|
||||||
StatusError,
|
StatusError,
|
||||||
DatabaseParseError,
|
DatabaseParseError,
|
||||||
NotFoundError,
|
isNotFoundError,
|
||||||
InternalInconsistencyError,
|
InternalInconsistencyError,
|
||||||
} from '../lib/errors';
|
} from '../lib/errors';
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
@ -145,12 +145,12 @@ export async function normaliseLegacyDatabase() {
|
|||||||
const imageFromDocker = await docker
|
const imageFromDocker = await docker
|
||||||
.getImage(service.image)
|
.getImage(service.image)
|
||||||
.inspect()
|
.inspect()
|
||||||
.catch((error) => {
|
.catch((e: unknown) => {
|
||||||
if (error instanceof NotFoundError) {
|
if (isNotFoundError(e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw e;
|
||||||
});
|
});
|
||||||
const imagesFromDatabase = await db
|
const imagesFromDatabase = await db
|
||||||
.models('image')
|
.models('image')
|
||||||
|
@ -9,7 +9,7 @@ const rimrafAsync = Bluebird.promisify(rimraf);
|
|||||||
import * as volumeManager from '../compose/volume-manager';
|
import * as volumeManager from '../compose/volume-manager';
|
||||||
import * as deviceState from '../device-state';
|
import * as deviceState from '../device-state';
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { BackupError, NotFoundError } from '../lib/errors';
|
import { BackupError, isNotFoundError } from '../lib/errors';
|
||||||
import { exec, pathExistsOnHost, mkdirp } from '../lib/fs-utils';
|
import { exec, pathExistsOnHost, mkdirp } from '../lib/fs-utils';
|
||||||
import { log } from '../lib/supervisor-console';
|
import { log } from '../lib/supervisor-console';
|
||||||
|
|
||||||
@ -67,11 +67,11 @@ export async function loadBackupFromMigration(
|
|||||||
.then((volume) => {
|
.then((volume) => {
|
||||||
return volume.remove();
|
return volume.remove();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((e: unknown) => {
|
||||||
if (error instanceof NotFoundError) {
|
if (isNotFoundError(e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw error;
|
throw e;
|
||||||
});
|
});
|
||||||
|
|
||||||
await volumeManager.createFromPath(
|
await volumeManager.createFromPath(
|
||||||
|
@ -3,15 +3,7 @@ process.env.DOCKER_HOST = 'unix:///your/dockerode/mocks/are/not/working';
|
|||||||
import * as dockerode from 'dockerode';
|
import * as dockerode from 'dockerode';
|
||||||
import { Stream } from 'stream';
|
import { Stream } from 'stream';
|
||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
import { TypedError } from 'typed-error';
|
import { NotFoundError } from '~/lib/errors';
|
||||||
|
|
||||||
export class NotFoundError extends TypedError {
|
|
||||||
public statusCode: number;
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.statusCode = 404;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const overrides: Dictionary<(...args: any[]) => Resolvable<any>> = {};
|
const overrides: Dictionary<(...args: any[]) => Resolvable<any>> = {};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user