Recategorize some errors as expected.

Change-type: patch
Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
Scott Lowe
2020-06-26 13:46:58 +02:00
parent 17089a35c3
commit 1308b64c67
9 changed files with 37 additions and 33 deletions

View File

@ -117,7 +117,7 @@ export default class EnvAddCmd extends Command {
params.value = process.env[params.name]; params.value = process.env[params.name];
if (params.value == null) { if (params.value == null) {
throw new Error( throw new ExpectedError(
`Value not found for environment variable: ${params.name}`, `Value not found for environment variable: ${params.name}`,
); );
} else if (!options.quiet) { } else if (!options.quiet) {

View File

@ -51,7 +51,7 @@ const deployProject = function (docker, logger, composeOpts, opts) {
project.descriptors.length > 1 && project.descriptors.length > 1 &&
!opts.app.application_type?.[0]?.supports_multicontainer !opts.app.application_type?.[0]?.supports_multicontainer
) { ) {
throw new Error( throw new ExpectedError(
'Target application does not support multiple containers. Aborting!', 'Target application does not support multiple containers. Aborting!',
); );
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2017 Balena Copyright 2017-2020 Balena Ltd.
Licensed under the Apache License, Version 2.0 (the 'License'); Licensed under the Apache License, Version 2.0 (the 'License');
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -16,7 +16,9 @@ limitations under the License.
import type { CommandDefinition } from 'capitano'; import type { CommandDefinition } from 'capitano';
import type * as SDK from 'etcher-sdk'; import type * as SDK from 'etcher-sdk';
import { getChalk, getVisuals, stripIndent } from '../../utils/lazy'; import { getChalk, getVisuals, stripIndent } from '../../utils/lazy';
import { ExpectedError } from '../../errors';
async function getDrive(options: { async function getDrive(options: {
drive?: string; drive?: string;
@ -31,7 +33,7 @@ async function getDrive(options: {
try { try {
const d = scanner.getBy('device', drive); const d = scanner.getBy('device', drive);
if (d === undefined || !(d instanceof sdk.sourceDestination.BlockDevice)) { if (d === undefined || !(d instanceof sdk.sourceDestination.BlockDevice)) {
throw new Error(`Drive not found: ${options.drive}`); throw new ExpectedError(`Drive not found: ${options.drive}`);
} }
return d; return d;
} finally { } finally {

View File

@ -21,6 +21,7 @@ import { createServer, Server, Socket } from 'net';
import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { getOnlineTargetUuid } from '../utils/patterns'; import { getOnlineTargetUuid } from '../utils/patterns';
import { tunnelConnectionToDevice } from '../utils/tunnel'; import { tunnelConnectionToDevice } from '../utils/tunnel';
import { ExpectedError } from '../errors';
interface Args { interface Args {
deviceOrApplication: string; deviceOrApplication: string;
@ -223,7 +224,7 @@ export const tunnel: CommandDefinition<Args, Options> = {
const results = await Promise.all(localListeners); const results = await Promise.all(localListeners);
if (!results.includes(true)) { if (!results.includes(true)) {
throw new Error('No ports are valid for tunnelling'); throw new ExpectedError('No ports are valid for tunnelling');
} }
logger.logInfo('Waiting for connections...'); logger.logInfo('Waiting for connections...');

View File

@ -21,6 +21,7 @@ import type { Socket } from 'net';
import * as path from 'path'; import * as path from 'path';
import * as utils from './utils'; import * as utils from './utils';
import { ExpectedError } from '../errors';
const serverSockets: Socket[] = []; const serverSockets: Socket[] = [];
@ -87,11 +88,11 @@ export const awaitForToken = (options: {
try { try {
const token = request.body.token?.trim(); const token = request.body.token?.trim();
if (!token) { if (!token) {
throw new Error('No token'); throw new ExpectedError('No token');
} }
const loggedIn = await utils.loginIfTokenValid(token); const loggedIn = await utils.loginIfTokenValid(token);
if (!loggedIn) { if (!loggedIn) {
throw new Error('Invalid token'); throw new ExpectedError('Invalid token');
} }
response.status(200).render('success'); response.status(200).render('success');
resolve(token); resolve(token);

View File

@ -355,7 +355,7 @@ async function parseRegistrySecrets(
if (/.+\.ya?ml$/i.test(secretsFilename)) { if (/.+\.ya?ml$/i.test(secretsFilename)) {
isYaml = true; isYaml = true;
} else if (!/.+\.json$/i.test(secretsFilename)) { } else if (!/.+\.json$/i.test(secretsFilename)) {
throw new Error('Filename must end with .json, .yml or .yaml'); throw new ExpectedError('Filename must end with .json, .yml or .yaml');
} }
const raw = (await fs.readFile(secretsFilename)).toString(); const raw = (await fs.readFile(secretsFilename)).toString();
const registrySecrets = new MultiBuild.RegistrySecretValidator().validateRegistrySecrets( const registrySecrets = new MultiBuild.RegistrySecretValidator().validateRegistrySecrets(
@ -459,7 +459,7 @@ async function performResolution(
(clonedStream: tar.Pack) => { (clonedStream: tar.Pack) => {
buildTask.buildStream = clonedStream; buildTask.buildStream = clonedStream;
if (!buildTask.external && !buildTask.resolved) { if (!buildTask.external && !buildTask.resolved) {
throw new Error( throw new ExpectedError(
`Project type for service "${buildTask.serviceName}" could not be determined. Missing a Dockerfile?`, `Project type for service "${buildTask.serviceName}" could not be determined. Missing a Dockerfile?`,
); );
} }

View File

@ -19,6 +19,7 @@
import * as Bluebird from 'bluebird'; import * as Bluebird from 'bluebird';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { ExpectedError } from '../errors';
// Use this function to seed an action's list of capitano options // Use this function to seed an action's list of capitano options
// with the docker options. Using this interface means that // with the docker options. Using this interface means that
@ -122,7 +123,7 @@ const generateConnectOpts = function (opts) {
connectOpts.port = opts.dockerPort || 2376; connectOpts.port = opts.dockerPort || 2376;
} else if (opts.docker != null && opts.dockerHost != null) { } else if (opts.docker != null && opts.dockerHost != null) {
// Both provided, no obvious way to continue // Both provided, no obvious way to continue
throw new Error( throw new ExpectedError(
"Both a local docker socket and docker host have been provided. Don't know how to continue.", "Both a local docker socket and docker host have been provided. Don't know how to continue.",
); );
} else { } else {
@ -142,7 +143,7 @@ const generateConnectOpts = function (opts) {
if (opts.ca != null || opts.cert != null || opts.key != null) { if (opts.ca != null || opts.cert != null || opts.key != null) {
// but not all // but not all
if (!(opts.ca != null && opts.cert != null && opts.key != null)) { if (!(opts.ca != null && opts.cert != null && opts.key != null)) {
throw new Error( throw new ExpectedError(
'You must provide a CA, certificate and key in order to use TLS', 'You must provide a CA, certificate and key in order to use TLS',
); );
} }
@ -172,7 +173,7 @@ const parseBuildArgs = function (args) {
if (pair != null) { if (pair != null) {
buildArgs[pair[1]] = pair[2] ?? ''; buildArgs[pair[1]] = pair[2] ?? '';
} else { } else {
throw new Error(`Could not parse build argument: '${arg}'`); throw new ExpectedError(`Could not parse build argument: '${arg}'`);
} }
}); });
return buildArgs; return buildArgs;

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2016-2020 Balena Copyright 2016-2020 Balena Ltd.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -19,10 +19,14 @@ import Bluebird = require('bluebird');
import _ = require('lodash'); import _ = require('lodash');
import _form = require('resin-cli-form'); import _form = require('resin-cli-form');
import { exitWithExpectedError, instanceOf, NotLoggedInError } from '../errors'; import {
exitWithExpectedError,
instanceOf,
NotLoggedInError,
ExpectedError,
} from '../errors';
import { getBalenaSdk, getVisuals, stripIndent } from './lazy'; import { getBalenaSdk, getVisuals, stripIndent } from './lazy';
import validation = require('./validation'); import validation = require('./validation');
import { isV12 } from './version';
const getForm = _.once((): typeof _form => require('resin-cli-form')); const getForm = _.once((): typeof _form => require('resin-cli-form'));
@ -65,7 +69,7 @@ export function authenticate(options: {}): Bluebird<void> {
error.name === 'BalenaRequestError' && error.name === 'BalenaRequestError' &&
error.statusCode === 401 error.statusCode === 401
) { ) {
throw new Error('Invalid two factor authentication code'); throw new ExpectedError('Invalid two factor authentication code');
} }
throw error; throw error;
}); });
@ -80,16 +84,9 @@ export function authenticate(options: {}): Bluebird<void> {
export async function checkLoggedIn(): Promise<void> { export async function checkLoggedIn(): Promise<void> {
const balena = getBalenaSdk(); const balena = getBalenaSdk();
if (!(await balena.auth.isLoggedIn())) { if (!(await balena.auth.isLoggedIn())) {
if (isV12()) { throw new NotLoggedInError(stripIndent`
throw new NotLoggedInError(stripIndent` Login required: use the “balena login” command to log in.
Login required: use the “balena login” command to log in. `);
`);
} else {
throw new NotLoggedInError(stripIndent`
You have to log in to continue
Run the following command to go through the login wizard:
$ balena login`);
}
} }
} }
@ -175,7 +172,7 @@ export function selectApplication(
.hasAny() .hasAny()
.then(function (hasAnyApplications) { .then(function (hasAnyApplications) {
if (!hasAnyApplications) { if (!hasAnyApplications) {
throw new Error("You don't have any applications"); throw new ExpectedError("You don't have any applications");
} }
return balena.models.application.getAll(); return balena.models.application.getAll();
@ -316,7 +313,7 @@ export function inferOrSelectDevice(preferredUuid: string) {
.filter<BalenaSdk.Device>((device) => device.is_online) .filter<BalenaSdk.Device>((device) => device.is_online)
.then((onlineDevices) => { .then((onlineDevices) => {
if (_.isEmpty(onlineDevices)) { if (_.isEmpty(onlineDevices)) {
throw new Error("You don't have any devices online"); throw new ExpectedError("You don't have any devices online");
} }
const defaultUuid = _(onlineDevices).map('uuid').includes(preferredUuid) const defaultUuid = _(onlineDevices).map('uuid').includes(preferredUuid)
@ -348,7 +345,9 @@ export async function getOnlineTargetUuid(
const uuidTest = validation.validateUuid(applicationOrDevice); const uuidTest = validation.validateUuid(applicationOrDevice);
if (!appTest && !uuidTest) { if (!appTest && !uuidTest) {
throw new Error(`Device or application not found: ${applicationOrDevice}`); throw new ExpectedError(
`Device or application not found: ${applicationOrDevice}`,
);
} }
// if we have a definite device UUID... // if we have a definite device UUID...
@ -375,7 +374,7 @@ export async function getOnlineTargetUuid(
}); });
if (_.isEmpty(devices)) { if (_.isEmpty(devices)) {
throw new Error('No accessible devices are online'); throw new ExpectedError('No accessible devices are online');
} }
return await getForm().ask({ return await getForm().ask({

View File

@ -180,7 +180,7 @@ async function getOrSelectLocalDevice(deviceIp?: string): Promise<string> {
}); });
if (!ip) { if (!ip) {
throw new Error('No device selected'); throw new ExpectedError('No device selected');
} }
return ip; return ip;
@ -211,7 +211,7 @@ async function getOrSelectApplication(
const allDeviceTypes = await sdk.models.config.getDeviceTypes(); const allDeviceTypes = await sdk.models.config.getDeviceTypes();
const deviceTypeManifest = _.find(allDeviceTypes, { slug: deviceType }); const deviceTypeManifest = _.find(allDeviceTypes, { slug: deviceType });
if (!deviceTypeManifest) { if (!deviceTypeManifest) {
throw new Error(`"${deviceType}" is not a valid device type`); throw new ExpectedError(`"${deviceType}" is not a valid device type`);
} }
const compatibleDeviceTypes = _(allDeviceTypes) const compatibleDeviceTypes = _(allDeviceTypes)
.filter( .filter(
@ -274,7 +274,7 @@ async function getOrSelectApplication(
); );
if (validApplications.length === 0) { if (validApplications.length === 0) {
throw new Error('No application found with a matching device type'); throw new ExpectedError('No application found with a matching device type');
} }
if (validApplications.length === 1) { if (validApplications.length === 1) {