UNPKG

balena-cli

Version:

The official balena Command Line Interface

206 lines (177 loc) • 5.71 kB
/** * @license * Copyright 2017-2021 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, Command } from '@oclif/core'; import { getCliUx, stripIndent } from '../../utils/lazy'; import { pick } from '../../utils/helpers'; export default class DeviceDetectCmd extends Command { public static enableJsonFlag = true; public static description = stripIndent` Scan for balenaOS devices on your local network. Scan for balenaOS devices on your local network. The output includes device information collected through balenaEngine for devices running a development image of balenaOS. Devices running a production image do not expose balenaEngine (on TCP port 2375), which is why less information is printed about them. `; public static examples = [ '$ balena device detect', '$ balena device detect --timeout 120', '$ balena device detect --verbose', ]; public static flags = { verbose: Flags.boolean({ default: false, char: 'v', description: 'display full info', }), timeout: Flags.integer({ char: 't', description: 'scan timeout in seconds', }), }; public static primary = true; public static root = true; public static offlineCompatible = true; public async run() { const _ = await import('lodash'); const { discoverLocalBalenaOsDevices } = await import( '../../utils/discover' ); const prettyjson = await import('prettyjson'); const dockerUtils = await import('../../utils/docker'); const dockerPort = 2375; const dockerTimeout = 2000; const { flags: options } = await this.parse(DeviceDetectCmd); const discoverTimeout = options.timeout != null ? options.timeout * 1000 : undefined; // Find active local devices const ux = getCliUx(); ux.action.start('Scanning for local balenaOS devices'); const localDevices = await discoverLocalBalenaOsDevices(discoverTimeout); const engineReachableDevices: boolean[] = await Promise.all( localDevices.map(async ({ address }: { address: string }) => { const docker = await dockerUtils.createClient({ host: address, port: dockerPort, timeout: dockerTimeout, }); try { await docker.ping(); return true; } catch { return false; } }), ); const developmentDevices = localDevices.filter( (_localDevice, index) => engineReachableDevices[index], ); const productionDevices = _.differenceWith( localDevices, developmentDevices, _.isEqual, ); const productionDevicesInfo = productionDevices.map((device) => { return { host: device.host, address: device.address, osVariant: 'production', dockerInfo: undefined, dockerVersion: undefined, }; }); // Query devices for info const devicesInfo = await Promise.all( developmentDevices.map(async ({ host, address }) => { const docker = await dockerUtils.createClient({ host: address, port: dockerPort, timeout: dockerTimeout, }); const [dockerInfo, dockerVersion] = await Promise.all([ docker.info(), docker.version(), ]); return { host, address, osVariant: 'development', dockerInfo, dockerVersion, }; }), ); ux.action.stop('Reporting scan results'); // Reduce properties if not --verbose if (!options.verbose) { devicesInfo.forEach((d: any) => { d.dockerInfo = d.dockerInfo != null && typeof d.dockerInfo === 'object' ? pick(d.dockerInfo, DeviceDetectCmd.dockerInfoProperties) : d.dockerInfo; d.dockerVersion = d.dockerVersion != null && typeof d.dockerVersion === 'object' ? pick(d.dockerVersion, DeviceDetectCmd.dockerVersionProperties) : d.dockerVersion; }); } const cmdOutput: Array<{ host: string; address: string; osVariant: string; dockerInfo: any; dockerVersion: import('dockerode').DockerVersion | undefined; }> = [...productionDevicesInfo, ...devicesInfo]; // Output results if (!options.json && cmdOutput.length === 0) { console.error( process.platform === 'win32' ? DeviceDetectCmd.noDevicesFoundMessage + DeviceDetectCmd.windowsTipMessage : DeviceDetectCmd.noDevicesFoundMessage, ); return; } if (options.json) { return JSON.stringify(cmdOutput, null, 4); } console.log(prettyjson.render(cmdOutput, { 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 'device detect' 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 device detect' command again.`; }