mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-06-20 16:50:15 +00:00
Recategorize some errors as expected.
Change-type: patch Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
2
lib/actions-oclif/env/add.ts
vendored
2
lib/actions-oclif/env/add.ts
vendored
@ -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) {
|
||||||
|
@ -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!',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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...');
|
||||||
|
@ -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);
|
||||||
|
@ -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?`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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({
|
||||||
|
@ -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) {
|
||||||
|
Reference in New Issue
Block a user