Add ability to specify poll interval in join command

Change-type: minor
Resolves: #1432 #1697 #1670
Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
Scott Lowe
2020-09-09 14:20:57 +02:00
parent 9f8569e33f
commit 0ca1faba09
6 changed files with 50 additions and 8 deletions

View File

@ -2805,6 +2805,10 @@ the IP or hostname of device
application name application name
#### -i, --pollInterval POLLINTERVAL
the interval in minutes to check for updates
## leave [deviceIpOrHostname] ## leave [deviceIpOrHostname]
Remove a local device from its balena application, causing the device to Remove a local device from its balena application, causing the device to

View File

@ -19,9 +19,11 @@ import { flags } from '@oclif/command';
import Command from '../command'; import Command from '../command';
import * as cf from '../utils/common-flags'; import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { parseAsLocalHostnameOrIp } from '../utils/validation';
interface FlagsDef { interface FlagsDef {
application?: string; application?: string;
pollInterval?: number;
help?: void; help?: void;
} }
@ -61,6 +63,7 @@ export default class JoinCmd extends Command {
{ {
name: 'deviceIpOrHostname', name: 'deviceIpOrHostname',
description: 'the IP or hostname of device', description: 'the IP or hostname of device',
parse: parseAsLocalHostnameOrIp,
}, },
]; ];
@ -72,6 +75,10 @@ export default class JoinCmd extends Command {
description: 'the name of the application the device should join', description: 'the name of the application the device should join',
...cf.application, ...cf.application,
}, },
pollInterval: flags.integer({
description: 'the interval in minutes to check for updates',
char: 'i',
}),
help: cf.help, help: cf.help,
}; };
@ -83,15 +90,15 @@ export default class JoinCmd extends Command {
JoinCmd, JoinCmd,
); );
const Logger = await import('../utils/logger');
const promote = await import('../utils/promote'); const promote = await import('../utils/promote');
const sdk = getBalenaSdk(); const sdk = getBalenaSdk();
const logger = Logger.getLogger(); const logger = await Command.getLogger();
return promote.join( return promote.join(
logger, logger,
sdk, sdk,
params.deviceIpOrHostname, params.deviceIpOrHostname,
options.application, options.application,
options.pollInterval,
); );
} }
} }

View File

@ -19,6 +19,7 @@ import { flags } from '@oclif/command';
import Command from '../command'; import Command from '../command';
import * as cf from '../utils/common-flags'; import * as cf from '../utils/common-flags';
import { getBalenaSdk, stripIndent } from '../utils/lazy'; import { getBalenaSdk, stripIndent } from '../utils/lazy';
import { parseAsLocalHostnameOrIp } from '../utils/validation';
interface FlagsDef { interface FlagsDef {
help?: void; help?: void;
@ -54,10 +55,10 @@ export default class LeaveCmd extends Command {
{ {
name: 'deviceIpOrHostname', name: 'deviceIpOrHostname',
description: 'the device IP or hostname', description: 'the device IP or hostname',
parse: parseAsLocalHostnameOrIp,
}, },
]; ];
// Hardcoded to preserve camelcase
public static usage = 'leave [deviceIpOrHostname]'; public static usage = 'leave [deviceIpOrHostname]';
public static flags: flags.Input<FlagsDef> = { public static flags: flags.Input<FlagsDef> = {
@ -70,10 +71,9 @@ export default class LeaveCmd extends Command {
public async run() { public async run() {
const { args: params } = this.parse<FlagsDef, ArgsDef>(LeaveCmd); const { args: params } = this.parse<FlagsDef, ArgsDef>(LeaveCmd);
const Logger = await import('../utils/logger');
const promote = await import('../utils/promote'); const promote = await import('../utils/promote');
const sdk = getBalenaSdk(); const sdk = getBalenaSdk();
const logger = Logger.getLogger(); const logger = await Command.getLogger();
return promote.leave(logger, sdk, params.deviceIpOrHostname); return promote.leave(logger, sdk, params.deviceIpOrHostname);
} }
} }

View File

@ -88,7 +88,11 @@ export async function generateBaseConfig(
export async function generateApplicationConfig( export async function generateApplicationConfig(
application: BalenaSdk.Application, application: BalenaSdk.Application,
options: { version: string; deviceType?: string }, options: {
version: string;
deviceType?: string;
appUpdatePollInterval?: number;
},
) { ) {
const config = await generateBaseConfig(application, options); const config = await generateBaseConfig(application, options);

View File

@ -28,6 +28,7 @@ export async function join(
sdk: BalenaSdk.BalenaSDK, sdk: BalenaSdk.BalenaSDK,
deviceHostnameOrIp?: string, deviceHostnameOrIp?: string,
appName?: string, appName?: string,
appUpdatePollInterval?: number,
): Promise<void> { ): Promise<void> {
logger.logDebug('Determining device...'); logger.logDebug('Determining device...');
const deviceIp = await getOrSelectLocalDevice(deviceHostnameOrIp); const deviceIp = await getOrSelectLocalDevice(deviceHostnameOrIp);
@ -55,6 +56,7 @@ export async function join(
logger.logDebug('Generating application config...'); logger.logDebug('Generating application config...');
const config = await generateApplicationConfig(sdk, app, { const config = await generateApplicationConfig(sdk, app, {
version: deviceOsVersion, version: deviceOsVersion,
appUpdatePollInterval,
}); });
logger.logDebug(`Using config: ${JSON.stringify(config, null, 2)}`); logger.logDebug(`Using config: ${JSON.stringify(config, null, 2)}`);
@ -387,7 +389,10 @@ async function createApplication(
async function generateApplicationConfig( async function generateApplicationConfig(
sdk: BalenaSdk.BalenaSDK, sdk: BalenaSdk.BalenaSDK,
app: ApplicationWithDeviceType, app: ApplicationWithDeviceType,
options: { version: string }, options: {
version: string;
appUpdatePollInterval?: number;
},
) { ) {
const { generateApplicationConfig: configGen } = await import('./config'); const { generateApplicationConfig: configGen } = await import('./config');
@ -397,8 +402,13 @@ async function generateApplicationConfig(
const opts = const opts =
manifest.options && manifest.options &&
manifest.options.filter((opt) => opt.name !== 'network'); manifest.options.filter((opt) => opt.name !== 'network');
const override = {
appUpdatePollInterval: options.appUpdatePollInterval,
};
const values = { const values = {
...(opts ? await getCliForm().run(opts) : {}), ...(opts ? await getCliForm().run(opts, { override }) : {}),
...options, ...options,
}; };

View File

@ -57,6 +57,10 @@ export function validateDotLocalUrl(input: string): boolean {
return DOTLOCAL_REGEX.test(input); return DOTLOCAL_REGEX.test(input);
} }
export function validateLocalHostnameOrIp(input: string): boolean {
return validateIPAddress(input) || validateDotLocalUrl(input);
}
export function validateLongUuid(input: string): boolean { export function validateLongUuid(input: string): boolean {
if (input.length !== 32 && input.length !== 62) { if (input.length !== 32 && input.length !== 62) {
return false; return false;
@ -100,3 +104,16 @@ export function tryAsInteger(input: string): number | string {
return input; return input;
} }
} }
export function parseAsLocalHostnameOrIp(input: string, paramName?: string) {
if (input && !validateLocalHostnameOrIp(input)) {
const message =
paramName == null
? 'The parameter must be a local hostname or IP address.'
: `The parameter '${paramName}' must be a local hostname or IP address.`;
throw new ExpectedError(message);
}
return input;
}