mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-18 10:46:34 +00:00
Merge pull request #1871 from balena-io/convert-devices
Convert device commands to oclif
This commit is contained in:
commit
795057338f
@ -51,6 +51,15 @@ const capitanoDoc = {
|
||||
title: 'Device',
|
||||
files: [
|
||||
'build/actions/device.js',
|
||||
'build/actions-oclif/device/identify.js',
|
||||
'build/actions-oclif/device/index.js',
|
||||
'build/actions-oclif/device/move.js',
|
||||
'build/actions-oclif/device/reboot.js',
|
||||
'build/actions-oclif/device/register.js',
|
||||
'build/actions-oclif/device/rename.js',
|
||||
'build/actions-oclif/device/rm.js',
|
||||
'build/actions-oclif/device/shutdown.js',
|
||||
'build/actions-oclif/devices/index.js',
|
||||
'build/actions-oclif/devices/supported.js',
|
||||
'build/actions-oclif/device/public-url.js',
|
||||
],
|
||||
|
335
doc/cli.markdown
335
doc/cli.markdown
@ -167,17 +167,17 @@ Users are encouraged to regularly update the balena CLI to the latest version.
|
||||
|
||||
- Device
|
||||
|
||||
- [devices](#devices)
|
||||
- [device <uuid>](#device-uuid)
|
||||
- [device register <application>](#device-register-application)
|
||||
- [device rm <uuid>](#device-rm-uuid)
|
||||
- [device identify <uuid>](#device-identify-uuid)
|
||||
- [device reboot <uuid>](#device-reboot-uuid)
|
||||
- [device shutdown <uuid>](#device-shutdown-uuid)
|
||||
- [device rename <uuid> [newName]](#device-rename-uuid-newname)
|
||||
- [device move <uuid>](#device-move-uuid)
|
||||
- [device init](#device-init)
|
||||
- [device os-update <uuid>](#device-os-update-uuid)
|
||||
- [device identify <uuid>](#device-identify-uuid)
|
||||
- [device <uuid>](#device-uuid)
|
||||
- [device move <uuid>](#device-move-uuid)
|
||||
- [device reboot <uuid>](#device-reboot-uuid)
|
||||
- [device register <application>](#device-register-application)
|
||||
- [device rename <uuid> [newname]](#device-rename-uuid-newname)
|
||||
- [device rm <uuid>](#device-rm-uuid)
|
||||
- [device shutdown <uuid>](#device-shutdown-uuid)
|
||||
- [devices](#devices)
|
||||
- [devices supported](#devices-supported)
|
||||
- [device public-url <uuid>](#device-public-url-uuid)
|
||||
|
||||
@ -454,132 +454,6 @@ Examples:
|
||||
|
||||
# Device
|
||||
|
||||
## devices
|
||||
|
||||
Use this command to list all devices that belong to you.
|
||||
|
||||
You can filter the devices by application by using the `--application` option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena devices
|
||||
$ balena devices --application MyApp
|
||||
$ balena devices --app MyApp
|
||||
$ balena devices -a MyApp
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
## device <uuid>
|
||||
|
||||
Use this command to show information about a single device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device 7cf02a6
|
||||
|
||||
## device register <application>
|
||||
|
||||
Use this command to register a device to an application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device register MyApp
|
||||
$ balena device register MyApp --uuid <uuid>
|
||||
|
||||
### Options
|
||||
|
||||
#### --uuid, -u <uuid>
|
||||
|
||||
custom uuid
|
||||
|
||||
## device rm <uuid>
|
||||
|
||||
Use this command to remove a device from balena.
|
||||
|
||||
Notice this command asks for confirmation interactively.
|
||||
You can avoid this by passing the `--yes` boolean option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device rm 7cf02a6
|
||||
$ balena device rm 7cf02a6 --yes
|
||||
|
||||
### Options
|
||||
|
||||
#### --yes, -y
|
||||
|
||||
confirm non interactively
|
||||
|
||||
## device identify <uuid>
|
||||
|
||||
Use this command to identify a device.
|
||||
|
||||
In the Raspberry Pi, the ACT led is blinked several times.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device identify 23c73a1
|
||||
|
||||
## device reboot <uuid>
|
||||
|
||||
Use this command to remotely reboot a device
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device reboot 23c73a1
|
||||
|
||||
### Options
|
||||
|
||||
#### --force, -f
|
||||
|
||||
force action if the update lock is set
|
||||
|
||||
## device shutdown <uuid>
|
||||
|
||||
Use this command to remotely shutdown a device
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device shutdown 23c73a1
|
||||
|
||||
### Options
|
||||
|
||||
#### --force, -f
|
||||
|
||||
force action if the update lock is set
|
||||
|
||||
## device rename <uuid> [newName]
|
||||
|
||||
Use this command to rename a device.
|
||||
|
||||
If you omit the name, you'll get asked for it interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device rename 7cf02a6
|
||||
$ balena device rename 7cf02a6 MyPi
|
||||
|
||||
## device move <uuid>
|
||||
|
||||
Use this command to move a device to another application you own.
|
||||
|
||||
If you omit the application, you'll get asked for it interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device move 7cf02a6
|
||||
$ balena device move 7cf02a6 --application MyNewApp
|
||||
|
||||
### Options
|
||||
|
||||
#### --application, -a, --app <application>
|
||||
|
||||
application name
|
||||
|
||||
## device init
|
||||
|
||||
Use this command to download the OS image of a certain application and write it to an SD Card.
|
||||
@ -646,6 +520,197 @@ a balenaOS version
|
||||
|
||||
confirm non interactively
|
||||
|
||||
## device identify <uuid>
|
||||
|
||||
Identify a device by making the ACT LED blink (Raspberry Pi).
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device identify 23c73a1
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the uuid of the device to identify
|
||||
|
||||
### Options
|
||||
|
||||
## device <uuid>
|
||||
|
||||
Show information about a single device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device 7cf02a6
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the device uuid
|
||||
|
||||
### Options
|
||||
|
||||
## device move <uuid>
|
||||
|
||||
Move a device to another application.
|
||||
|
||||
Note, if the application option is omitted it will be prompted
|
||||
for interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device move 7cf02a6
|
||||
$ balena device move 7cf02a6 --application MyNewApp
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the uuid of the device to move
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
application name
|
||||
|
||||
#### --app APP
|
||||
|
||||
same as '--application'
|
||||
|
||||
## device reboot <uuid>
|
||||
|
||||
Remotely reboot a device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device reboot 23c73a1
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the uuid of the device to reboot
|
||||
|
||||
### Options
|
||||
|
||||
#### -f, --force
|
||||
|
||||
force action if the update lock is set
|
||||
|
||||
## device register <application>
|
||||
|
||||
Register a device to an application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device register MyApp
|
||||
$ balena device register MyApp --uuid <uuid>
|
||||
|
||||
### Arguments
|
||||
|
||||
#### APPLICATION
|
||||
|
||||
the name or id of application to register device with
|
||||
|
||||
### Options
|
||||
|
||||
#### -u, --uuid UUID
|
||||
|
||||
custom uuid
|
||||
|
||||
## device rename <uuid> [newName]
|
||||
|
||||
Rename a device.
|
||||
|
||||
Note, if the name is omitted, it will be prompted for interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device rename 7cf02a6
|
||||
$ balena device rename 7cf02a6 MyPi
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the uuid of the device to rename
|
||||
|
||||
#### NEWNAME
|
||||
|
||||
the new name for the device
|
||||
|
||||
### Options
|
||||
|
||||
## device rm <uuid>
|
||||
|
||||
Remove a device from balena.
|
||||
|
||||
Note this command asks for confirmation interactively.
|
||||
You can avoid this by passing the `--yes` option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device rm 7cf02a6
|
||||
$ balena device rm 7cf02a6 --yes
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the uuid of the device to remove
|
||||
|
||||
### Options
|
||||
|
||||
#### -y, --yes
|
||||
|
||||
answer "yes" to all questions (non interactive use)
|
||||
|
||||
## device shutdown <uuid>
|
||||
|
||||
Remotely shutdown a device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device shutdown 23c73a1
|
||||
|
||||
### Arguments
|
||||
|
||||
#### UUID
|
||||
|
||||
the uuid of the device to shutdown
|
||||
|
||||
### Options
|
||||
|
||||
#### -f, --force
|
||||
|
||||
force action if the update lock is set
|
||||
|
||||
## devices
|
||||
|
||||
list all devices that belong to you.
|
||||
|
||||
You can filter the devices by application by using the `--application` option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena devices
|
||||
$ balena devices --application MyApp
|
||||
$ balena devices --app MyApp
|
||||
$ balena devices -a MyApp
|
||||
|
||||
### Options
|
||||
|
||||
#### -a, --application APPLICATION
|
||||
|
||||
application name
|
||||
|
||||
#### --app APP
|
||||
|
||||
same as '--application'
|
||||
|
||||
## devices supported
|
||||
|
||||
List the supported device types (like 'raspberrypi3' or 'intel-nuc').
|
||||
|
75
lib/actions-oclif/device/identify.ts
Normal file
75
lib/actions-oclif/device/identify.ts
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import { ExpectedError } from '../../errors';
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export default class DeviceIdentifyCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Identify a device.
|
||||
|
||||
Identify a device by making the ACT LED blink (Raspberry Pi).
|
||||
`;
|
||||
public static examples = ['$ balena device identify 23c73a1'];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the uuid of the device to identify',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device identify <uuid>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceIdentifyCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
try {
|
||||
await balena.models.device.identify(params.uuid);
|
||||
} catch (e) {
|
||||
if (e.message === 'Request error: No online device(s) found') {
|
||||
throw new ExpectedError(`Device ${params.uuid} is not online`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
lib/actions-oclif/device/index.ts
Normal file
110
lib/actions-oclif/device/index.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import { Application, Device } from 'balena-sdk';
|
||||
|
||||
interface ExtendedDevice extends Device {
|
||||
dashboard_url?: string;
|
||||
application_name?: string;
|
||||
commit?: string;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export default class DeviceCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Show info about a single device.
|
||||
|
||||
Show information about a single device.
|
||||
`;
|
||||
public static examples = ['$ balena device 7cf02a6'];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the device uuid',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device <uuid>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
public static primary = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
const device: ExtendedDevice = await balena.models.device.get(
|
||||
params.uuid,
|
||||
expandForAppName,
|
||||
);
|
||||
|
||||
const deviceStatus = await balena.models.device.getStatus(device.uuid);
|
||||
device.status = deviceStatus;
|
||||
|
||||
device.dashboard_url = balena.models.device.getDashboardUrl(device.uuid);
|
||||
|
||||
const belongsToApplication = device.belongs_to__application as Application[];
|
||||
device.application_name = belongsToApplication?.[0]
|
||||
? belongsToApplication[0].app_name
|
||||
: 'N/a';
|
||||
|
||||
device.commit = device.is_on__commit;
|
||||
|
||||
console.log(
|
||||
getVisuals().table.vertical(device, [
|
||||
`$${device.device_name}$`,
|
||||
'id',
|
||||
'device_type',
|
||||
'status',
|
||||
'is_online',
|
||||
'ip_address',
|
||||
'application_name',
|
||||
'last_seen',
|
||||
'uuid',
|
||||
'commit',
|
||||
'supervisor_version',
|
||||
'is_web_accessible',
|
||||
'note',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
131
lib/actions-oclif/device/move.ts
Normal file
131
lib/actions-oclif/device/move.ts
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { Application, Device } from 'balena-sdk';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import * as _ from 'lodash';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
|
||||
interface ExtendedDevice extends Device {
|
||||
application_name?: string;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export default class DeviceMoveCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Move a device to another application.
|
||||
|
||||
Move a device to another application.
|
||||
|
||||
Note, if the application option is omitted it will be prompted
|
||||
for interactively.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena device move 7cf02a6',
|
||||
'$ balena device move 7cf02a6 --application MyNewApp',
|
||||
];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the uuid of the device to move',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device move <uuid>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceMoveCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const patterns = await import('../../utils/patterns');
|
||||
|
||||
// Consolidate application options
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
|
||||
const device: ExtendedDevice = await balena.models.device.get(
|
||||
params.uuid,
|
||||
expandForAppName,
|
||||
);
|
||||
|
||||
const belongsToApplication = device.belongs_to__application as Application[];
|
||||
device.application_name = belongsToApplication?.[0]
|
||||
? belongsToApplication[0].app_name
|
||||
: 'N/a';
|
||||
|
||||
// Get destination application
|
||||
let application;
|
||||
if (options.application) {
|
||||
application = options.application;
|
||||
} else {
|
||||
const [deviceDeviceType, deviceTypes] = await Promise.all([
|
||||
balena.models.device.getManifestBySlug(device.device_type),
|
||||
balena.models.config.getDeviceTypes(),
|
||||
]);
|
||||
|
||||
const compatibleDeviceTypes = deviceTypes.filter(
|
||||
(dt) =>
|
||||
balena.models.os.isArchitectureCompatibleWith(
|
||||
deviceDeviceType.arch,
|
||||
dt.arch,
|
||||
) &&
|
||||
!!dt.isDependent === !!deviceDeviceType.isDependent &&
|
||||
dt.state !== 'DISCONTINUED',
|
||||
);
|
||||
|
||||
application = await patterns.selectApplication((app: Application) =>
|
||||
_.every([
|
||||
_.some(compatibleDeviceTypes, (dt) => dt.slug === app.device_type),
|
||||
// @ts-ignore using the extended device object prop
|
||||
device.application_name !== app.app_name,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
await balena.models.device.move(params.uuid, tryAsInteger(application));
|
||||
|
||||
console.info(`${params.uuid} was moved to ${application}`);
|
||||
}
|
||||
}
|
73
lib/actions-oclif/device/reboot.ts
Normal file
73
lib/actions-oclif/device/reboot.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
|
||||
interface FlagsDef {
|
||||
force: boolean;
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export default class DeviceRebootCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Restart a device.
|
||||
|
||||
Remotely reboot a device.
|
||||
`;
|
||||
public static examples = ['$ balena device reboot 23c73a1'];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the uuid of the device to reboot',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device reboot <uuid>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
force: cf.force,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceRebootCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
// The SDK current throws "BalenaDeviceNotFound: Device not found: xxxxx"
|
||||
// when the device is not online, which may be confusing.
|
||||
// https://github.com/balena-io/balena-cli/issues/1872
|
||||
await balena.models.device.reboot(params.uuid, options);
|
||||
}
|
||||
}
|
83
lib/actions-oclif/device/register.ts
Normal file
83
lib/actions-oclif/device/register.ts
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
|
||||
interface FlagsDef {
|
||||
uuid?: string;
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
application: string;
|
||||
}
|
||||
|
||||
export default class DeviceRegisterCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Register a device.
|
||||
|
||||
Register a device to an application.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena device register MyApp',
|
||||
'$ balena device register MyApp --uuid <uuid>',
|
||||
];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'application',
|
||||
description: 'the name or id of application to register device with',
|
||||
parse: (app) => tryAsInteger(app),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device register <application>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
uuid: flags.string({
|
||||
description: 'custom uuid',
|
||||
char: 'u',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceRegisterCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
const application = await balena.models.application.get(params.application);
|
||||
const uuid = options.uuid ?? balena.models.device.generateUniqueKey();
|
||||
|
||||
console.info(`Registering to ${application.app_name}: ${uuid}`);
|
||||
|
||||
const result = await balena.models.device.register(application.id, uuid);
|
||||
|
||||
return result && result.uuid;
|
||||
}
|
||||
}
|
85
lib/actions-oclif/device/rename.ts
Normal file
85
lib/actions-oclif/device/rename.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
|
||||
interface FlagsDef {
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
newName?: string;
|
||||
}
|
||||
|
||||
export default class DeviceRenameCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Rename a device.
|
||||
|
||||
Rename a device.
|
||||
|
||||
Note, if the name is omitted, it will be prompted for interactively.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena device rename 7cf02a6',
|
||||
'$ balena device rename 7cf02a6 MyPi',
|
||||
];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the uuid of the device to rename',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'newName',
|
||||
description: 'the new name for the device',
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device rename <uuid> [newName]';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(DeviceRenameCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const form = await import('resin-cli-form');
|
||||
|
||||
const newName =
|
||||
params.newName ||
|
||||
(await form.ask({
|
||||
message: 'How do you want to name this device?',
|
||||
type: 'input',
|
||||
})) ||
|
||||
'';
|
||||
|
||||
await balena.models.device.rename(params.uuid, newName);
|
||||
}
|
||||
}
|
84
lib/actions-oclif/device/rm.ts
Normal file
84
lib/actions-oclif/device/rm.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
|
||||
interface FlagsDef {
|
||||
yes: boolean;
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export default class DeviceRmCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Remove a device.
|
||||
|
||||
Remove a device from balena.
|
||||
|
||||
Note this command asks for confirmation interactively.
|
||||
You can avoid this by passing the \`--yes\` option.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena device rm 7cf02a6',
|
||||
'$ balena device rm 7cf02a6 --yes',
|
||||
];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the uuid of the device to remove',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device rm <uuid>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
yes: cf.yes,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceRmCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
const patterns = await import('../../utils/patterns');
|
||||
|
||||
// Confirm
|
||||
await patterns.confirm(
|
||||
options.yes,
|
||||
'Are you sure you want to delete the device?',
|
||||
);
|
||||
|
||||
// Remove
|
||||
await balena.models.device.remove(params.uuid);
|
||||
}
|
||||
}
|
79
lib/actions-oclif/device/shutdown.ts
Normal file
79
lib/actions-oclif/device/shutdown.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { IArg } from '@oclif/parser/lib/args';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { getBalenaSdk } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import { ExpectedError } from '../../errors';
|
||||
|
||||
interface FlagsDef {
|
||||
force: boolean;
|
||||
help: void;
|
||||
}
|
||||
|
||||
interface ArgsDef {
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export default class DeviceShutdownCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Shutdown a device.
|
||||
|
||||
Remotely shutdown a device.
|
||||
`;
|
||||
public static examples = ['$ balena device shutdown 23c73a1'];
|
||||
|
||||
public static args: Array<IArg<any>> = [
|
||||
{
|
||||
name: 'uuid',
|
||||
description: 'the uuid of the device to shutdown',
|
||||
parse: (dev) => tryAsInteger(dev),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
public static usage = 'device shutdown <uuid>';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
force: cf.force,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
|
||||
DeviceShutdownCmd,
|
||||
);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
try {
|
||||
await balena.models.device.shutdown(params.uuid, options);
|
||||
} catch (e) {
|
||||
if (e.message === 'Request error: No online device(s) found') {
|
||||
throw new ExpectedError(`Device ${params.uuid} is not online`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
lib/actions-oclif/devices/index.ts
Normal file
113
lib/actions-oclif/devices/index.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @license
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { flags } from '@oclif/command';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import * as _ from 'lodash';
|
||||
import Command from '../../command';
|
||||
import * as cf from '../../utils/common-flags';
|
||||
import { expandForAppName } from '../../utils/helpers';
|
||||
import { getBalenaSdk, getVisuals } from '../../utils/lazy';
|
||||
import { tryAsInteger } from '../../utils/validation';
|
||||
import { Device, Application } from 'balena-sdk';
|
||||
|
||||
interface ExtendedDevice extends Device {
|
||||
dashboard_url?: string;
|
||||
application_name?: string;
|
||||
}
|
||||
|
||||
interface FlagsDef {
|
||||
application?: string;
|
||||
app?: string;
|
||||
help: void;
|
||||
}
|
||||
|
||||
export default class DevicesCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
List all devices.
|
||||
|
||||
list all devices that belong to you.
|
||||
|
||||
You can filter the devices by application by using the \`--application\` option.
|
||||
`;
|
||||
public static examples = [
|
||||
'$ balena devices',
|
||||
'$ balena devices --application MyApp',
|
||||
'$ balena devices --app MyApp',
|
||||
'$ balena devices -a MyApp',
|
||||
];
|
||||
|
||||
public static usage = 'devices';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
application: cf.application,
|
||||
app: cf.app,
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
|
||||
public static authenticated = true;
|
||||
|
||||
public async run() {
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(DevicesCmd);
|
||||
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
// Consolidate application options
|
||||
options.application = options.application || options.app;
|
||||
delete options.app;
|
||||
|
||||
let devices: ExtendedDevice[];
|
||||
|
||||
if (options.application != null) {
|
||||
devices = await balena.models.device.getAllByApplication(
|
||||
tryAsInteger(options.application),
|
||||
expandForAppName,
|
||||
);
|
||||
} else {
|
||||
devices = await balena.models.device.getAll(expandForAppName);
|
||||
}
|
||||
|
||||
devices = _.map(devices, function (device) {
|
||||
device.dashboard_url = balena.models.device.getDashboardUrl(device.uuid);
|
||||
|
||||
const belongsToApplication = device.belongs_to__application as Application[];
|
||||
device.application_name = belongsToApplication?.[0]
|
||||
? belongsToApplication[0].app_name
|
||||
: 'N/a';
|
||||
|
||||
device.uuid = device.uuid.slice(0, 7);
|
||||
return device;
|
||||
});
|
||||
|
||||
console.log(
|
||||
getVisuals().table.horizontal(devices, [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
'application_name',
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
@ -17,351 +17,7 @@ limitations under the License.
|
||||
import * as commandOptions from './command-options';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import { normalizeUuidProp } from '../utils/normalization';
|
||||
import { getBalenaSdk, getVisuals } from '../utils/lazy';
|
||||
|
||||
/** @type {import('balena-sdk').PineOptionsFor<import('balena-sdk').Device>} */
|
||||
const expandForAppName = {
|
||||
$expand: { belongs_to__application: { $select: 'app_name' } },
|
||||
};
|
||||
|
||||
export const list = {
|
||||
signature: 'devices',
|
||||
description: 'list all devices',
|
||||
help: `\
|
||||
Use this command to list all devices that belong to you.
|
||||
|
||||
You can filter the devices by application by using the \`--application\` option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena devices
|
||||
$ balena devices --application MyApp
|
||||
$ balena devices --app MyApp
|
||||
$ balena devices -a MyApp\
|
||||
`,
|
||||
options: [commandOptions.optionalApplication],
|
||||
permission: 'user',
|
||||
primary: true,
|
||||
action(_params, options) {
|
||||
const Promise = require('bluebird');
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
return Promise.try(function () {
|
||||
if (options.application != null) {
|
||||
return balena.models.device.getAllByApplication(
|
||||
options.application,
|
||||
expandForAppName,
|
||||
);
|
||||
}
|
||||
return balena.models.device.getAll(expandForAppName);
|
||||
}).tap(function (devices) {
|
||||
devices = _.map(devices, function (device) {
|
||||
// @ts-ignore extending the device object with extra props
|
||||
device.dashboard_url = balena.models.device.getDashboardUrl(
|
||||
device.uuid,
|
||||
);
|
||||
// @ts-ignore extending the device object with extra props
|
||||
device.application_name = device.belongs_to__application?.[0]
|
||||
? device.belongs_to__application[0].app_name
|
||||
: 'N/a';
|
||||
device.uuid = device.uuid.slice(0, 7);
|
||||
return device;
|
||||
});
|
||||
|
||||
console.log(
|
||||
getVisuals().table.horizontal(devices, [
|
||||
'id',
|
||||
'uuid',
|
||||
'device_name',
|
||||
'device_type',
|
||||
'application_name',
|
||||
'status',
|
||||
'is_online',
|
||||
'supervisor_version',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
]),
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const info = {
|
||||
signature: 'device <uuid>',
|
||||
description: 'list a single device',
|
||||
help: `\
|
||||
Use this command to show information about a single device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device 7cf02a6\
|
||||
`,
|
||||
permission: 'user',
|
||||
primary: true,
|
||||
action(params) {
|
||||
normalizeUuidProp(params);
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
return balena.models.device
|
||||
.get(params.uuid, expandForAppName)
|
||||
.then((device) =>
|
||||
// @ts-ignore `device.getStatus` requires a device with service info, but
|
||||
// this device isn't typed with them, possibly needs fixing?
|
||||
balena.models.device.getStatus(params.uuid).then(function (status) {
|
||||
device.status = status;
|
||||
// @ts-ignore extending the device object with extra props
|
||||
device.dashboard_url = balena.models.device.getDashboardUrl(
|
||||
device.uuid,
|
||||
);
|
||||
// @ts-ignore extending the device object with extra props
|
||||
device.application_name = device.belongs_to__application?.[0]
|
||||
? device.belongs_to__application[0].app_name
|
||||
: 'N/a';
|
||||
// @ts-ignore extending the device object with extra props
|
||||
device.commit = device.is_on__commit;
|
||||
|
||||
console.log(
|
||||
getVisuals().table.vertical(device, [
|
||||
`$${device.device_name}$`,
|
||||
'id',
|
||||
'device_type',
|
||||
'status',
|
||||
'is_online',
|
||||
'ip_address',
|
||||
'mac_address',
|
||||
'application_name',
|
||||
'last_seen',
|
||||
'uuid',
|
||||
'commit',
|
||||
'supervisor_version',
|
||||
'is_web_accessible',
|
||||
'note',
|
||||
'os_version',
|
||||
'dashboard_url',
|
||||
]),
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const register = {
|
||||
signature: 'device register <application>',
|
||||
description: 'register a device',
|
||||
help: `\
|
||||
Use this command to register a device to an application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device register MyApp
|
||||
$ balena device register MyApp --uuid <uuid>\
|
||||
`,
|
||||
permission: 'user',
|
||||
options: [
|
||||
{
|
||||
signature: 'uuid',
|
||||
description: 'custom uuid',
|
||||
parameter: 'uuid',
|
||||
alias: 'u',
|
||||
},
|
||||
],
|
||||
action(params, options) {
|
||||
const Promise = require('bluebird');
|
||||
const balena = getBalenaSdk();
|
||||
|
||||
return Promise.join(
|
||||
balena.models.application.get(params.application),
|
||||
options.uuid ?? balena.models.device.generateUniqueKey(),
|
||||
function (application, uuid) {
|
||||
console.info(`Registering to ${application.app_name}: ${uuid}`);
|
||||
return balena.models.device.register(application.id, uuid);
|
||||
},
|
||||
).get('uuid');
|
||||
},
|
||||
};
|
||||
|
||||
export const remove = {
|
||||
signature: 'device rm <uuid>',
|
||||
description: 'remove a device',
|
||||
help: `\
|
||||
Use this command to remove a device from balena.
|
||||
|
||||
Notice this command asks for confirmation interactively.
|
||||
You can avoid this by passing the \`--yes\` boolean option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device rm 7cf02a6
|
||||
$ balena device rm 7cf02a6 --yes\
|
||||
`,
|
||||
options: [commandOptions.yes],
|
||||
permission: 'user',
|
||||
action(params, options) {
|
||||
normalizeUuidProp(params);
|
||||
const balena = getBalenaSdk();
|
||||
const patterns = require('../utils/patterns');
|
||||
|
||||
return patterns
|
||||
.confirm(options.yes, 'Are you sure you want to delete the device?')
|
||||
.then(() => balena.models.device.remove(params.uuid));
|
||||
},
|
||||
};
|
||||
|
||||
export const identify = {
|
||||
signature: 'device identify <uuid>',
|
||||
description: 'identify a device with a UUID',
|
||||
help: `\
|
||||
Use this command to identify a device.
|
||||
|
||||
In the Raspberry Pi, the ACT led is blinked several times.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device identify 23c73a1\
|
||||
`,
|
||||
permission: 'user',
|
||||
action(params) {
|
||||
normalizeUuidProp(params);
|
||||
const balena = getBalenaSdk();
|
||||
return balena.models.device.identify(params.uuid);
|
||||
},
|
||||
};
|
||||
|
||||
export const reboot = {
|
||||
signature: 'device reboot <uuid>',
|
||||
description: 'restart a device',
|
||||
help: `\
|
||||
Use this command to remotely reboot a device
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device reboot 23c73a1\
|
||||
`,
|
||||
options: [commandOptions.forceUpdateLock],
|
||||
permission: 'user',
|
||||
action(params, options) {
|
||||
normalizeUuidProp(params);
|
||||
const balena = getBalenaSdk();
|
||||
return balena.models.device.reboot(params.uuid, options);
|
||||
},
|
||||
};
|
||||
|
||||
export const shutdown = {
|
||||
signature: 'device shutdown <uuid>',
|
||||
description: 'shutdown a device',
|
||||
help: `\
|
||||
Use this command to remotely shutdown a device
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device shutdown 23c73a1\
|
||||
`,
|
||||
options: [commandOptions.forceUpdateLock],
|
||||
permission: 'user',
|
||||
action(params, options) {
|
||||
normalizeUuidProp(params);
|
||||
const balena = getBalenaSdk();
|
||||
return balena.models.device.shutdown(params.uuid, options);
|
||||
},
|
||||
};
|
||||
|
||||
export const rename = {
|
||||
signature: 'device rename <uuid> [newName]',
|
||||
description: 'rename a balena device',
|
||||
help: `\
|
||||
Use this command to rename a device.
|
||||
|
||||
If you omit the name, you'll get asked for it interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device rename 7cf02a6
|
||||
$ balena device rename 7cf02a6 MyPi\
|
||||
`,
|
||||
permission: 'user',
|
||||
action(params) {
|
||||
normalizeUuidProp(params);
|
||||
const Promise = require('bluebird');
|
||||
const balena = getBalenaSdk();
|
||||
const form = require('resin-cli-form');
|
||||
|
||||
return Promise.try(function () {
|
||||
if (!_.isEmpty(params.newName)) {
|
||||
return params.newName;
|
||||
}
|
||||
|
||||
return form.ask({
|
||||
message: 'How do you want to name this device?',
|
||||
type: 'input',
|
||||
});
|
||||
}).then(_.partial(balena.models.device.rename, params.uuid));
|
||||
},
|
||||
};
|
||||
|
||||
export const move = {
|
||||
signature: 'device move <uuid>',
|
||||
description: 'move a device to another application',
|
||||
help: `\
|
||||
Use this command to move a device to another application you own.
|
||||
|
||||
If you omit the application, you'll get asked for it interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena device move 7cf02a6
|
||||
$ balena device move 7cf02a6 --application MyNewApp\
|
||||
`,
|
||||
permission: 'user',
|
||||
options: [commandOptions.optionalApplication],
|
||||
action(params, options) {
|
||||
normalizeUuidProp(params);
|
||||
const balena = getBalenaSdk();
|
||||
const patterns = require('../utils/patterns');
|
||||
|
||||
return balena.models.device
|
||||
.get(params.uuid, expandForAppName)
|
||||
.then(function (device) {
|
||||
// @ts-ignore extending the device object with extra props
|
||||
device.application_name = device.belongs_to__application?.[0]
|
||||
? device.belongs_to__application[0].app_name
|
||||
: 'N/a';
|
||||
if (options.application) {
|
||||
return options.application;
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
balena.models.device.getManifestBySlug(device.device_type),
|
||||
balena.models.config.getDeviceTypes(),
|
||||
]).then(function ([deviceDeviceType, deviceTypes]) {
|
||||
const compatibleDeviceTypes = deviceTypes.filter(
|
||||
(dt) =>
|
||||
balena.models.os.isArchitectureCompatibleWith(
|
||||
deviceDeviceType.arch,
|
||||
dt.arch,
|
||||
) &&
|
||||
!!dt.isDependent === !!deviceDeviceType.isDependent &&
|
||||
dt.state !== 'DISCONTINUED',
|
||||
);
|
||||
|
||||
return patterns.selectApplication((application) =>
|
||||
_.every([
|
||||
_.some(
|
||||
compatibleDeviceTypes,
|
||||
(dt) => dt.slug === application.device_type,
|
||||
),
|
||||
// @ts-ignore using the extended device object prop
|
||||
device.application_name !== application.app_name,
|
||||
]),
|
||||
);
|
||||
});
|
||||
})
|
||||
.tap((application) => balena.models.device.move(params.uuid, application))
|
||||
.then((application) => {
|
||||
console.info(`${params.uuid} was moved to ${application}`);
|
||||
});
|
||||
},
|
||||
};
|
||||
import { getBalenaSdk } from '../utils/lazy';
|
||||
|
||||
export const init = {
|
||||
signature: 'device init',
|
||||
|
@ -53,17 +53,8 @@ capitano.command(actions.auth.logout);
|
||||
capitano.command(actions.auth.whoami);
|
||||
|
||||
// ---------- Device Module ----------
|
||||
capitano.command(actions.device.list);
|
||||
capitano.command(actions.device.rename);
|
||||
capitano.command(actions.device.init);
|
||||
capitano.command(actions.device.remove);
|
||||
capitano.command(actions.device.identify);
|
||||
capitano.command(actions.device.reboot);
|
||||
capitano.command(actions.device.shutdown);
|
||||
capitano.command(actions.device.register);
|
||||
capitano.command(actions.device.move);
|
||||
capitano.command(actions.device.osUpdate);
|
||||
capitano.command(actions.device.info);
|
||||
|
||||
// ---------- OS Module ----------
|
||||
capitano.command(actions.os.versions);
|
||||
|
@ -139,7 +139,8 @@ const EXPECTED_ERROR_REGEXES = [
|
||||
/^BalenaApplicationNotFound/, // balena-sdk
|
||||
/^BalenaDeviceNotFound/, // balena-sdk
|
||||
/^BalenaExpiredToken/, // balena-sdk
|
||||
/^Missing \w+$/, // Capitano, oclif parser: RequiredArgsError, RequiredFlagError
|
||||
/^Missing \w+$/, // Capitano,
|
||||
/^Missing \d required argument/, // oclif parser: RequiredArgsError, RequiredFlagError
|
||||
/^Unexpected argument/, // oclif parser: UnexpectedArgsError
|
||||
/to be one of/, // oclif parser: FlagInvalidOptionError, ArgInvalidOptionError
|
||||
];
|
||||
|
@ -141,7 +141,16 @@ export const convertedCommands = [
|
||||
'app:rm',
|
||||
'apps',
|
||||
'api-key:generate',
|
||||
'device',
|
||||
'device:identify',
|
||||
'device:move',
|
||||
'device:public-url',
|
||||
'device:reboot',
|
||||
'device:register',
|
||||
'device:rename',
|
||||
'device:rm',
|
||||
'device:shutdown',
|
||||
'devices',
|
||||
'devices:supported',
|
||||
'envs',
|
||||
'env:add',
|
||||
|
@ -23,6 +23,10 @@ export const application = flags.string({
|
||||
char: 'a',
|
||||
description: 'application name',
|
||||
});
|
||||
// TODO: Consider remove second alias 'app' when we can, to simplify.
|
||||
export const app = flags.string({
|
||||
description: "same as '--application'",
|
||||
});
|
||||
|
||||
export const device = flags.string({
|
||||
char: 'd',
|
||||
@ -56,3 +60,8 @@ export const yes: IBooleanFlag<boolean> = flags.boolean({
|
||||
char: 'y',
|
||||
description: 'answer "yes" to all questions (non interactive use)',
|
||||
});
|
||||
|
||||
export const force: IBooleanFlag<boolean> = flags.boolean({
|
||||
char: 'f',
|
||||
description: 'force action if the update lock is set',
|
||||
});
|
||||
|
@ -22,6 +22,7 @@ import * as _ from 'lodash';
|
||||
import * as os from 'os';
|
||||
import * as ShellEscape from 'shell-escape';
|
||||
|
||||
import { Device, PineOptionsFor } from 'balena-sdk';
|
||||
import { ExpectedError } from '../errors';
|
||||
import { getBalenaSdk, getChalk, getVisuals } from './lazy';
|
||||
|
||||
@ -466,3 +467,7 @@ export function getProxyConfig(): ProxyConfig | undefined {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const expandForAppName: PineOptionsFor<Device> = {
|
||||
$expand: { belongs_to__application: { $select: 'app_name' } },
|
||||
};
|
||||
|
@ -20,20 +20,28 @@ import { BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
const HELP_RESPONSE = `
|
||||
Usage: device move <uuid>
|
||||
Move a device to another application.
|
||||
|
||||
Use this command to move a device to another application you own.
|
||||
USAGE
|
||||
$ balena device move <uuid>
|
||||
|
||||
If you omit the application, you'll get asked for it interactively.
|
||||
ARGUMENTS
|
||||
<uuid> the uuid of the device to move
|
||||
|
||||
Examples:
|
||||
OPTIONS
|
||||
-a, --application <application> application name
|
||||
-h, --help show CLI help
|
||||
--app <app> same as '--application'
|
||||
|
||||
\t$ balena device move 7cf02a6
|
||||
\t$ balena device move 7cf02a6 --application MyNewApp
|
||||
DESCRIPTION
|
||||
Move a device to another application.
|
||||
|
||||
Options:
|
||||
Note, if the application option is omitted it will be prompted
|
||||
for interactively.
|
||||
|
||||
--application, -a, --app <application> application name
|
||||
EXAMPLES
|
||||
$ balena device move 7cf02a6
|
||||
$ balena device move 7cf02a6 --application MyNewApp
|
||||
`;
|
||||
|
||||
describe('balena device move', function () {
|
||||
@ -49,7 +57,7 @@ describe('balena device move', function () {
|
||||
});
|
||||
|
||||
it('should print help text with the -h flag', async () => {
|
||||
api.expectGetWhoAmI({ optional: true });
|
||||
api.expectGetWhoAmI({ optional: true, persist: true });
|
||||
api.expectGetMixpanel({ optional: true });
|
||||
|
||||
const { out, err } = await runCommand('device move -h');
|
||||
@ -59,16 +67,15 @@ describe('balena device move', function () {
|
||||
expect(err).to.eql([]);
|
||||
});
|
||||
|
||||
it.skip('should error if uuid not provided', async () => {
|
||||
// TODO: Figure out how to test for expected errors with current setup
|
||||
// including exit codes if possible.
|
||||
api.expectGetWhoAmI({ optional: true });
|
||||
it('should error if uuid not provided', async () => {
|
||||
api.expectGetWhoAmI({ optional: true, persist: true });
|
||||
api.expectGetMixpanel({ optional: true });
|
||||
|
||||
const { out, err } = await runCommand('device move');
|
||||
const errLines = cleanOutput(err);
|
||||
|
||||
expect(errLines[0]).to.equal('Missing uuid');
|
||||
expect(errLines[0]).to.equal('Missing 1 required argument:');
|
||||
expect(errLines[1]).to.equal('uuid : the uuid of the device to move');
|
||||
expect(out).to.eql([]);
|
||||
});
|
||||
|
||||
|
@ -22,13 +22,22 @@ import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
const HELP_RESPONSE = `
|
||||
Usage: device <uuid>
|
||||
Show info about a single device.
|
||||
|
||||
Use this command to show information about a single device.
|
||||
USAGE
|
||||
$ balena device <uuid>
|
||||
|
||||
Examples:
|
||||
ARGUMENTS
|
||||
<uuid> the device uuid
|
||||
|
||||
\t$ balena device 7cf02a6
|
||||
OPTIONS
|
||||
-h, --help show CLI help
|
||||
|
||||
DESCRIPTION
|
||||
Show information about a single device.
|
||||
|
||||
EXAMPLE
|
||||
$ balena device 7cf02a6
|
||||
`;
|
||||
|
||||
describe('balena device', function () {
|
||||
@ -44,7 +53,7 @@ describe('balena device', function () {
|
||||
});
|
||||
|
||||
it('should print help text with the -h flag', async () => {
|
||||
api.expectGetWhoAmI({ optional: true });
|
||||
api.expectGetWhoAmI({ optional: true, persist: true });
|
||||
api.expectGetMixpanel({ optional: true });
|
||||
|
||||
const { out, err } = await runCommand('device -h');
|
||||
@ -54,16 +63,15 @@ describe('balena device', function () {
|
||||
expect(err).to.eql([]);
|
||||
});
|
||||
|
||||
it.skip('should error if uuid not provided', async () => {
|
||||
// TODO: Figure out how to test for expected errors with current setup
|
||||
// including exit codes if possible.
|
||||
api.expectGetWhoAmI({ optional: true });
|
||||
it('should error if uuid not provided', async () => {
|
||||
api.expectGetWhoAmI({ optional: true, persist: true });
|
||||
api.expectGetMixpanel({ optional: true });
|
||||
|
||||
const { out, err } = await runCommand('device');
|
||||
const errLines = cleanOutput(err);
|
||||
|
||||
expect(errLines[0]).to.equal('Missing uuid');
|
||||
expect(errLines[0]).to.equal('Missing 1 required argument:');
|
||||
expect(errLines[1]).to.equal('uuid : the device uuid');
|
||||
expect(out).to.eql([]);
|
||||
});
|
||||
|
||||
|
@ -22,22 +22,26 @@ import { apiResponsePath, BalenaAPIMock } from '../../balena-api-mock';
|
||||
import { cleanOutput, runCommand } from '../../helpers';
|
||||
|
||||
const HELP_RESPONSE = `
|
||||
Usage: devices
|
||||
List all devices.
|
||||
|
||||
Use this command to list all devices that belong to you.
|
||||
USAGE
|
||||
$ balena devices
|
||||
|
||||
You can filter the devices by application by using the \`--application\` option.
|
||||
OPTIONS
|
||||
-a, --application <application> application name
|
||||
-h, --help show CLI help
|
||||
--app <app> same as '--application'
|
||||
|
||||
Examples:
|
||||
DESCRIPTION
|
||||
list all devices that belong to you.
|
||||
|
||||
\t$ balena devices
|
||||
\t$ balena devices --application MyApp
|
||||
\t$ balena devices --app MyApp
|
||||
\t$ balena devices -a MyApp
|
||||
You can filter the devices by application by using the \`--application\` option.
|
||||
|
||||
Options:
|
||||
|
||||
--application, -a, --app <application> application name
|
||||
EXAMPLES
|
||||
$ balena devices
|
||||
$ balena devices --application MyApp
|
||||
$ balena devices --app MyApp
|
||||
$ balena devices -a MyApp
|
||||
`;
|
||||
|
||||
describe('balena devices', function () {
|
||||
@ -53,7 +57,7 @@ describe('balena devices', function () {
|
||||
});
|
||||
|
||||
it('should print help text with the -h flag', async () => {
|
||||
api.expectGetWhoAmI({ optional: true });
|
||||
api.expectGetWhoAmI({ optional: true, persist: true });
|
||||
api.expectGetMixpanel({ optional: true });
|
||||
|
||||
const { out, err } = await runCommand('devices -h');
|
||||
|
@ -14,7 +14,7 @@ Primary commands:
|
||||
apps list all applications
|
||||
app <name> display information about a single application
|
||||
devices list all devices
|
||||
device <uuid> list a single device
|
||||
device <uuid> show info about a single device
|
||||
tunnel <deviceOrApplication> Tunnel local ports to your balenaOS device
|
||||
preload <image> preload an app on a disk image (or Edison zip archive)
|
||||
build [source] Build a single image or a multicontainer project locally
|
||||
@ -37,14 +37,14 @@ Additional commands:
|
||||
config read read a device configuration
|
||||
config reconfigure reconfigure a provisioned device
|
||||
config write <key> <value> write a device configuration
|
||||
device identify <uuid> identify a device with a UUID
|
||||
device identify <uuid> identify a device
|
||||
device init initialise a device with balenaOS
|
||||
device move <uuid> move a device to another application
|
||||
device os-update <uuid> Start a Host OS update for a device
|
||||
device public-url <uuid> get or manage the public URL for a device
|
||||
device reboot <uuid> restart a device
|
||||
device register <application> register a device
|
||||
device rename <uuid> [newName] rename a balena device
|
||||
device rename <uuid> [newname] rename a device
|
||||
device rm <uuid> remove a device
|
||||
device shutdown <uuid> shutdown a device
|
||||
devices supported list the supported device types (like 'raspberrypi3' or 'intel-nuc')
|
||||
|
@ -117,8 +117,9 @@ describe('handleError() function', () => {
|
||||
});
|
||||
|
||||
const messagesToMatch = [
|
||||
'Missing argument',
|
||||
'Missing arguments',
|
||||
'Missing uuid', // Capitano
|
||||
'Missing 1 required argument', // oclif
|
||||
'Missing 2 required arguments', // oclif
|
||||
'Unexpected argument',
|
||||
'Unexpected arguments',
|
||||
'to be one of',
|
||||
|
Loading…
Reference in New Issue
Block a user