From 1308b64c67f4b0f8d665e086971ed1e7549e22d8 Mon Sep 17 00:00:00 2001 From: Scott Lowe Date: Fri, 26 Jun 2020 13:46:58 +0200 Subject: [PATCH] Recategorize some errors as expected. Change-type: patch Signed-off-by: Scott Lowe --- lib/actions-oclif/env/add.ts | 2 +- lib/actions/deploy.js | 2 +- lib/actions/local/flash.ts | 6 ++++-- lib/actions/tunnel.ts | 3 ++- lib/auth/server.ts | 5 +++-- lib/utils/compose_ts.ts | 4 ++-- lib/utils/docker-js.js | 7 ++++--- lib/utils/patterns.ts | 35 +++++++++++++++++------------------ lib/utils/promote.ts | 6 +++--- 9 files changed, 37 insertions(+), 33 deletions(-) diff --git a/lib/actions-oclif/env/add.ts b/lib/actions-oclif/env/add.ts index 032202bc..8dbadc1a 100644 --- a/lib/actions-oclif/env/add.ts +++ b/lib/actions-oclif/env/add.ts @@ -117,7 +117,7 @@ export default class EnvAddCmd extends Command { params.value = process.env[params.name]; if (params.value == null) { - throw new Error( + throw new ExpectedError( `Value not found for environment variable: ${params.name}`, ); } else if (!options.quiet) { diff --git a/lib/actions/deploy.js b/lib/actions/deploy.js index bd194ab7..a4c5a8c3 100644 --- a/lib/actions/deploy.js +++ b/lib/actions/deploy.js @@ -51,7 +51,7 @@ const deployProject = function (docker, logger, composeOpts, opts) { project.descriptors.length > 1 && !opts.app.application_type?.[0]?.supports_multicontainer ) { - throw new Error( + throw new ExpectedError( 'Target application does not support multiple containers. Aborting!', ); } diff --git a/lib/actions/local/flash.ts b/lib/actions/local/flash.ts index 8ceb19d9..33831852 100644 --- a/lib/actions/local/flash.ts +++ b/lib/actions/local/flash.ts @@ -1,5 +1,5 @@ /* -Copyright 2017 Balena +Copyright 2017-2020 Balena Ltd. Licensed under the Apache License, Version 2.0 (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 * as SDK from 'etcher-sdk'; + import { getChalk, getVisuals, stripIndent } from '../../utils/lazy'; +import { ExpectedError } from '../../errors'; async function getDrive(options: { drive?: string; @@ -31,7 +33,7 @@ async function getDrive(options: { try { const d = scanner.getBy('device', drive); 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; } finally { diff --git a/lib/actions/tunnel.ts b/lib/actions/tunnel.ts index 461f933a..74898c8b 100644 --- a/lib/actions/tunnel.ts +++ b/lib/actions/tunnel.ts @@ -21,6 +21,7 @@ import { createServer, Server, Socket } from 'net'; import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { getOnlineTargetUuid } from '../utils/patterns'; import { tunnelConnectionToDevice } from '../utils/tunnel'; +import { ExpectedError } from '../errors'; interface Args { deviceOrApplication: string; @@ -223,7 +224,7 @@ export const tunnel: CommandDefinition = { const results = await Promise.all(localListeners); 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...'); diff --git a/lib/auth/server.ts b/lib/auth/server.ts index a3f60480..33f8e36a 100644 --- a/lib/auth/server.ts +++ b/lib/auth/server.ts @@ -21,6 +21,7 @@ import type { Socket } from 'net'; import * as path from 'path'; import * as utils from './utils'; +import { ExpectedError } from '../errors'; const serverSockets: Socket[] = []; @@ -87,11 +88,11 @@ export const awaitForToken = (options: { try { const token = request.body.token?.trim(); if (!token) { - throw new Error('No token'); + throw new ExpectedError('No token'); } const loggedIn = await utils.loginIfTokenValid(token); if (!loggedIn) { - throw new Error('Invalid token'); + throw new ExpectedError('Invalid token'); } response.status(200).render('success'); resolve(token); diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index 856299e3..bc6d612b 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -355,7 +355,7 @@ async function parseRegistrySecrets( if (/.+\.ya?ml$/i.test(secretsFilename)) { isYaml = true; } 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 registrySecrets = new MultiBuild.RegistrySecretValidator().validateRegistrySecrets( @@ -459,7 +459,7 @@ async function performResolution( (clonedStream: tar.Pack) => { buildTask.buildStream = clonedStream; if (!buildTask.external && !buildTask.resolved) { - throw new Error( + throw new ExpectedError( `Project type for service "${buildTask.serviceName}" could not be determined. Missing a Dockerfile?`, ); } diff --git a/lib/utils/docker-js.js b/lib/utils/docker-js.js index 9ab59272..f9c74ba2 100644 --- a/lib/utils/docker-js.js +++ b/lib/utils/docker-js.js @@ -19,6 +19,7 @@ import * as Bluebird from 'bluebird'; import * as _ from 'lodash'; +import { ExpectedError } from '../errors'; // Use this function to seed an action's list of capitano options // with the docker options. Using this interface means that @@ -122,7 +123,7 @@ const generateConnectOpts = function (opts) { connectOpts.port = opts.dockerPort || 2376; } else if (opts.docker != null && opts.dockerHost != null) { // 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.", ); } else { @@ -142,7 +143,7 @@ const generateConnectOpts = function (opts) { if (opts.ca != null || opts.cert != null || opts.key != null) { // but not all 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', ); } @@ -172,7 +173,7 @@ const parseBuildArgs = function (args) { if (pair != null) { buildArgs[pair[1]] = pair[2] ?? ''; } else { - throw new Error(`Could not parse build argument: '${arg}'`); + throw new ExpectedError(`Could not parse build argument: '${arg}'`); } }); return buildArgs; diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index fc6b495f..f0070d81 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -1,5 +1,5 @@ /* -Copyright 2016-2020 Balena +Copyright 2016-2020 Balena Ltd. Licensed under the Apache License, Version 2.0 (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 _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 validation = require('./validation'); -import { isV12 } from './version'; const getForm = _.once((): typeof _form => require('resin-cli-form')); @@ -65,7 +69,7 @@ export function authenticate(options: {}): Bluebird { error.name === 'BalenaRequestError' && error.statusCode === 401 ) { - throw new Error('Invalid two factor authentication code'); + throw new ExpectedError('Invalid two factor authentication code'); } throw error; }); @@ -80,16 +84,9 @@ export function authenticate(options: {}): Bluebird { export async function checkLoggedIn(): Promise { const balena = getBalenaSdk(); if (!(await balena.auth.isLoggedIn())) { - if (isV12()) { - throw new NotLoggedInError(stripIndent` - 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`); - } + throw new NotLoggedInError(stripIndent` + Login required: use the “balena login” command to log in. + `); } } @@ -175,7 +172,7 @@ export function selectApplication( .hasAny() .then(function (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(); @@ -316,7 +313,7 @@ export function inferOrSelectDevice(preferredUuid: string) { .filter((device) => device.is_online) .then((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) @@ -348,7 +345,9 @@ export async function getOnlineTargetUuid( const uuidTest = validation.validateUuid(applicationOrDevice); 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... @@ -375,7 +374,7 @@ export async function getOnlineTargetUuid( }); if (_.isEmpty(devices)) { - throw new Error('No accessible devices are online'); + throw new ExpectedError('No accessible devices are online'); } return await getForm().ask({ diff --git a/lib/utils/promote.ts b/lib/utils/promote.ts index 311ebc70..27c894bb 100644 --- a/lib/utils/promote.ts +++ b/lib/utils/promote.ts @@ -180,7 +180,7 @@ async function getOrSelectLocalDevice(deviceIp?: string): Promise { }); if (!ip) { - throw new Error('No device selected'); + throw new ExpectedError('No device selected'); } return ip; @@ -211,7 +211,7 @@ async function getOrSelectApplication( const allDeviceTypes = await sdk.models.config.getDeviceTypes(); const deviceTypeManifest = _.find(allDeviceTypes, { slug: deviceType }); 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) .filter( @@ -274,7 +274,7 @@ async function getOrSelectApplication( ); 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) {