mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-19 21:57:51 +00:00
Convert command scan
to TypeScript, migrate to oclif
Change-type: patch Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
parent
e3672bc655
commit
a2b761ec4b
@ -82,7 +82,7 @@ const capitanoDoc = {
|
||||
{
|
||||
title: 'Network',
|
||||
files: [
|
||||
'build/actions/scan.js',
|
||||
'build/actions-oclif/scan.js',
|
||||
'build/actions/ssh.js',
|
||||
'build/actions/tunnel.js',
|
||||
],
|
||||
|
@ -1182,6 +1182,7 @@ Only show system logs. This can be used in combination with --service.
|
||||
|
||||
## scan
|
||||
|
||||
Scan for balenaOS devices on your local network.
|
||||
|
||||
Examples:
|
||||
|
||||
@ -1191,13 +1192,13 @@ Examples:
|
||||
|
||||
### Options
|
||||
|
||||
#### --verbose, -v
|
||||
#### -v, --verbose
|
||||
|
||||
Display full info
|
||||
display full info
|
||||
|
||||
#### --timeout, -t <timeout>
|
||||
#### -t, --timeout TIMEOUT
|
||||
|
||||
Scan timeout in seconds
|
||||
scan timeout in seconds
|
||||
|
||||
## ssh <applicationOrDevice> [serviceName]
|
||||
|
||||
|
174
lib/actions-oclif/scan.ts
Normal file
174
lib/actions-oclif/scan.ts
Normal file
@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 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 { LocalBalenaOsDevice } from 'balena-sync';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import Command from '../command';
|
||||
import * as cf from '../utils/common-flags';
|
||||
import { getVisuals } from '../utils/lazy';
|
||||
|
||||
interface FlagsDef {
|
||||
verbose: boolean;
|
||||
timeout?: number;
|
||||
help: void;
|
||||
}
|
||||
|
||||
export default class ScanCmd extends Command {
|
||||
public static description = stripIndent`
|
||||
Scan for balenaOS devices on your local network.
|
||||
|
||||
Scan for balenaOS devices on your local network.
|
||||
`;
|
||||
|
||||
public static examples = [
|
||||
'$ balena scan',
|
||||
'$ balena scan --timeout 120',
|
||||
'$ balena scan --verbose',
|
||||
];
|
||||
|
||||
public static usage = 'scan';
|
||||
|
||||
public static flags: flags.Input<FlagsDef> = {
|
||||
verbose: flags.boolean({
|
||||
char: 'v',
|
||||
default: false,
|
||||
description: 'display full info',
|
||||
}),
|
||||
timeout: flags.integer({
|
||||
char: 't',
|
||||
description: 'scan timeout in seconds',
|
||||
}),
|
||||
help: cf.help,
|
||||
};
|
||||
|
||||
public static primary = true;
|
||||
public static root = true;
|
||||
|
||||
public async run() {
|
||||
const Bluebird = await import('bluebird');
|
||||
const _ = await import('lodash');
|
||||
const { SpinnerPromise } = getVisuals();
|
||||
const { discover } = await import('balena-sync');
|
||||
const prettyjson = await import('prettyjson');
|
||||
const { ExpectedError } = await import('../errors');
|
||||
const { dockerPort, dockerTimeout } = await import(
|
||||
'../actions/local/common'
|
||||
);
|
||||
const dockerUtils = await import('../utils/docker');
|
||||
|
||||
const { flags: options } = this.parse<FlagsDef, {}>(ScanCmd);
|
||||
|
||||
const discoverTimeout =
|
||||
options.timeout != null ? options.timeout * 1000 : undefined;
|
||||
|
||||
// Find active local devices
|
||||
const activeLocalDevices: LocalBalenaOsDevice[] = await new SpinnerPromise({
|
||||
promise: discover.discoverLocalBalenaOsDevices(discoverTimeout),
|
||||
startMessage: 'Scanning for local balenaOS devices..',
|
||||
stopMessage: 'Reporting scan results',
|
||||
}).filter(async ({ address }: { address: string }) => {
|
||||
const docker = dockerUtils.createClient({
|
||||
host: address,
|
||||
port: dockerPort,
|
||||
timeout: dockerTimeout,
|
||||
}) as any;
|
||||
try {
|
||||
await docker.pingAsync();
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Exit with message if no devices found
|
||||
if (_.isEmpty(activeLocalDevices)) {
|
||||
// TODO: Consider whether this should really be an error
|
||||
throw new ExpectedError(
|
||||
process.platform === 'win32'
|
||||
? ScanCmd.noDevicesFoundMessage + ScanCmd.windowsTipMessage
|
||||
: ScanCmd.noDevicesFoundMessage,
|
||||
);
|
||||
}
|
||||
|
||||
// Query devices for info
|
||||
const devicesInfo = await Bluebird.map(
|
||||
activeLocalDevices,
|
||||
({ host, address }) => {
|
||||
const docker = dockerUtils.createClient({
|
||||
host: address,
|
||||
port: dockerPort,
|
||||
timeout: dockerTimeout,
|
||||
}) as any;
|
||||
return Bluebird.props({
|
||||
host,
|
||||
address,
|
||||
dockerInfo: docker
|
||||
.infoAsync()
|
||||
.catchReturn('Could not get Docker info'),
|
||||
dockerVersion: docker
|
||||
.versionAsync()
|
||||
.catchReturn('Could not get Docker version'),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Reduce properties if not --verbose
|
||||
if (!options.verbose) {
|
||||
devicesInfo.forEach((d: any) => {
|
||||
d.dockerInfo = _.isObject(d.dockerInfo)
|
||||
? _.pick(d.dockerInfo, ScanCmd.dockerInfoProperties)
|
||||
: d.dockerInfo;
|
||||
d.dockerVersion = _.isObject(d.dockerVersion)
|
||||
? _.pick(d.dockerVersion, ScanCmd.dockerVersionProperties)
|
||||
: d.dockerVersion;
|
||||
});
|
||||
}
|
||||
|
||||
// Output results
|
||||
console.log(prettyjson.render(devicesInfo, { noColor: true }));
|
||||
}
|
||||
|
||||
protected static dockerInfoProperties = [
|
||||
'Containers',
|
||||
'ContainersRunning',
|
||||
'ContainersPaused',
|
||||
'ContainersStopped',
|
||||
'Images',
|
||||
'Driver',
|
||||
'SystemTime',
|
||||
'KernelVersion',
|
||||
'OperatingSystem',
|
||||
'Architecture',
|
||||
];
|
||||
|
||||
protected static dockerVersionProperties = ['Version', 'ApiVersion'];
|
||||
|
||||
protected static noDevicesFoundMessage =
|
||||
'Could not find any balenaOS devices on the local network.';
|
||||
|
||||
protected static windowsTipMessage = `
|
||||
|
||||
Note for Windows users:
|
||||
The 'scan' command relies on the Bonjour service. Check whether Bonjour is
|
||||
installed (Control Panel > Programs and Features). If not, you can download
|
||||
Bonjour for Windows (included with Bonjour Print Services) from here:
|
||||
https://support.apple.com/kb/DL999
|
||||
|
||||
After installing Bonjour, restart your PC and run the 'balena scan' command
|
||||
again.`;
|
||||
}
|
@ -22,7 +22,6 @@ module.exports =
|
||||
tags: require('./tags')
|
||||
logs: require('./logs')
|
||||
local: require('./local')
|
||||
scan: require('./scan')
|
||||
help: require('./help')
|
||||
os: require('./os')
|
||||
config: require('./config')
|
||||
|
19
lib/actions/local/common.d.ts
vendored
Normal file
19
lib/actions/local/common.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
export const dockerPort: number;
|
||||
export const dockerTimeout: number;
|
@ -1,115 +0,0 @@
|
||||
###
|
||||
Copyright 2017 Balena
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
{ getVisuals } = require('../utils/lazy')
|
||||
|
||||
dockerInfoProperties = [
|
||||
'Containers'
|
||||
'ContainersRunning'
|
||||
'ContainersPaused'
|
||||
'ContainersStopped'
|
||||
'Images'
|
||||
'Driver'
|
||||
'SystemTime'
|
||||
'KernelVersion'
|
||||
'OperatingSystem'
|
||||
'Architecture'
|
||||
]
|
||||
|
||||
dockerVersionProperties = [
|
||||
'Version'
|
||||
'ApiVersion'
|
||||
]
|
||||
|
||||
scanErrorMessage = 'Could not find any balenaOS devices in the local network.'
|
||||
|
||||
winScanErrorMessage = scanErrorMessage + """
|
||||
\n
|
||||
Note for Windows users:
|
||||
The 'scan' command relies on the Bonjour service. Check whether Bonjour is
|
||||
installed (Control Panel > Programs and Features). If not, you can download
|
||||
Bonjour for Windows (included with Bonjour Print Services) from here:
|
||||
https://support.apple.com/kb/DL999
|
||||
|
||||
After installing Bonjour, restart your PC and run the 'balena scan' command
|
||||
again.
|
||||
"""
|
||||
|
||||
module.exports =
|
||||
signature: 'scan'
|
||||
description: 'Scan for balenaOS devices in your local network'
|
||||
help: '''
|
||||
|
||||
Examples:
|
||||
|
||||
$ balena scan
|
||||
$ balena scan --timeout 120
|
||||
$ balena scan --verbose
|
||||
'''
|
||||
options: [
|
||||
signature: 'verbose'
|
||||
boolean: true
|
||||
description: 'Display full info'
|
||||
alias: 'v'
|
||||
,
|
||||
signature: 'timeout'
|
||||
parameter: 'timeout'
|
||||
description: 'Scan timeout in seconds'
|
||||
alias: 't'
|
||||
]
|
||||
primary: true
|
||||
root: true
|
||||
action: (params, options) ->
|
||||
Promise = require('bluebird')
|
||||
_ = require('lodash')
|
||||
prettyjson = require('prettyjson')
|
||||
{ discover } = require('balena-sync')
|
||||
{ SpinnerPromise } = getVisuals()
|
||||
{ dockerPort, dockerTimeout } = require('./local/common')
|
||||
dockerUtils = require('../utils/docker')
|
||||
{ exitWithExpectedError } = require('../utils/patterns')
|
||||
|
||||
if options.timeout?
|
||||
options.timeout *= 1000
|
||||
|
||||
Promise.try ->
|
||||
new SpinnerPromise
|
||||
promise: discover.discoverLocalBalenaOsDevices(options.timeout)
|
||||
startMessage: 'Scanning for local balenaOS devices..'
|
||||
stopMessage: 'Reporting scan results'
|
||||
.filter ({ address }) ->
|
||||
Promise.try ->
|
||||
docker = dockerUtils.createClient(host: address, port: dockerPort, timeout: dockerTimeout)
|
||||
docker.pingAsync()
|
||||
.return(true)
|
||||
.catchReturn(false)
|
||||
.tap (devices) ->
|
||||
if _.isEmpty(devices)
|
||||
exitWithExpectedError(if process.platform == 'win32' then winScanErrorMessage else scanErrorMessage)
|
||||
.map ({ host, address }) ->
|
||||
docker = dockerUtils.createClient(host: address, port: dockerPort, timeout: dockerTimeout)
|
||||
Promise.props
|
||||
dockerInfo: docker.infoAsync().catchReturn('Could not get Docker info')
|
||||
dockerVersion: docker.versionAsync().catchReturn('Could not get Docker version')
|
||||
.then ({ dockerInfo, dockerVersion }) ->
|
||||
|
||||
if not options.verbose
|
||||
dockerInfo = _.pick(dockerInfo, dockerInfoProperties) if _.isObject(dockerInfo)
|
||||
dockerVersion = _.pick(dockerVersion, dockerVersionProperties) if _.isObject(dockerVersion)
|
||||
|
||||
return { host, address, dockerInfo, dockerVersion }
|
||||
.then (devicesInfo) ->
|
||||
console.log(prettyjson.render(devicesInfo, noColor: true))
|
@ -107,7 +107,6 @@ capitano.command(actions.ssh.ssh)
|
||||
# ---------- Local balenaOS Module ----------
|
||||
capitano.command(actions.local.configure)
|
||||
capitano.command(actions.local.flash)
|
||||
capitano.command(actions.scan)
|
||||
|
||||
# ---------- Public utils ----------
|
||||
capitano.command(actions.util.availableDrives)
|
||||
@ -135,7 +134,7 @@ exports.run = (argv) ->
|
||||
# cmdSignature is literally a string like, for example:
|
||||
# "push <applicationOrDevice>"
|
||||
# ("applicationOrDevice" is NOT replaced with its actual value)
|
||||
# In case of failures like an inexistent or invalid command,
|
||||
# In case of failures like an nonexistent or invalid command,
|
||||
# command.signature.toString() returns '*'
|
||||
cmdSignature = command.signature.toString()
|
||||
events.trackCommand(cmdSignature)
|
||||
|
@ -143,6 +143,7 @@ export const convertedCommands = [
|
||||
'os:configure',
|
||||
'settings',
|
||||
'version',
|
||||
'scan',
|
||||
];
|
||||
|
||||
/**
|
||||
|
8
lib/utils/docker-coffee.d.ts
vendored
8
lib/utils/docker-coffee.d.ts
vendored
@ -30,8 +30,14 @@ export interface BuildDockerOptions {
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export interface DockerToolbeltOpts {
|
||||
host: string;
|
||||
port: number;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export function getDocker(
|
||||
options: BuildDockerOptions,
|
||||
): Bluebird<DockerToolbelt>;
|
||||
|
||||
export function createClient(options: BuildDockerOptions): DockerToolbelt;
|
||||
export function createClient(opts: DockerToolbeltOpts): DockerToolbelt;
|
||||
|
@ -27,7 +27,7 @@ Primary commands:
|
||||
deploy <appName> [image] Deploy a single image or a multicontainer project to a balena application
|
||||
join [deviceiporhostname] move a local device to an application on another balena server
|
||||
leave [deviceiporhostname] remove a local device from its balena application
|
||||
scan Scan for balenaOS devices in your local network
|
||||
scan scan for balenaOS devices on your local network
|
||||
|
||||
`;
|
||||
|
||||
@ -92,9 +92,20 @@ const GLOBAL_OPTIONS = `
|
||||
`;
|
||||
|
||||
describe('balena help', function() {
|
||||
it('should print simple help text', async () => {
|
||||
it('should list primary command summaries', async () => {
|
||||
const { out, err } = await runCommand('help');
|
||||
|
||||
console.log('ONE');
|
||||
console.log(cleanOutput(out));
|
||||
console.log(
|
||||
cleanOutput([
|
||||
SIMPLE_HELP,
|
||||
'Run `balena help --verbose` to list additional commands',
|
||||
GLOBAL_OPTIONS,
|
||||
]),
|
||||
);
|
||||
console.log();
|
||||
|
||||
expect(cleanOutput(out)).to.deep.equal(
|
||||
cleanOutput([
|
||||
SIMPLE_HELP,
|
||||
@ -106,7 +117,7 @@ describe('balena help', function() {
|
||||
expect(err.join('')).to.equal('');
|
||||
});
|
||||
|
||||
it('should print additional commands with the -v flag', async () => {
|
||||
it('should list all command summaries with the -v flag', async () => {
|
||||
const { out, err } = await runCommand('help -v');
|
||||
|
||||
expect(cleanOutput(out)).to.deep.equal(
|
||||
@ -118,7 +129,7 @@ describe('balena help', function() {
|
||||
expect(err.join('')).to.equal('');
|
||||
});
|
||||
|
||||
it('should print simple help text when no arguments present', async () => {
|
||||
it('should list primary command summaries', async () => {
|
||||
const { out, err } = await runCommand('');
|
||||
|
||||
expect(cleanOutput(out)).to.deep.equal(
|
||||
|
12
typings/balena-sync/index.d.ts
vendored
12
typings/balena-sync/index.d.ts
vendored
@ -20,9 +20,21 @@ declare module 'balena-sync' {
|
||||
|
||||
export function capitano(tool: 'balena-cli'): CommandDefinition;
|
||||
|
||||
export interface LocalBalenaOsDevice {
|
||||
address: string;
|
||||
host: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
declare namespace forms {
|
||||
export function selectLocalBalenaOsDevice(
|
||||
timeout?: number,
|
||||
): Promise<string>;
|
||||
}
|
||||
|
||||
declare namespace discover {
|
||||
export function discoverLocalBalenaOsDevices(
|
||||
timeout?: number,
|
||||
): Promise<LocalBalenaOsDevice[]>;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user