UNPKG

balena-cli

Version:

The official balena Command Line Interface

202 lines (195 loc) • 9.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const cf = require("../../utils/common-flags"); const lazy_1 = require("../../utils/lazy"); const messages_1 = require("../../utils/messages"); const helpers_1 = require("../../utils/helpers"); async function validateArgsAndOptions(options) { if (!options.fleet && !options.config) { const { ExpectedError } = await Promise.resolve().then(() => require('../../errors')); throw new ExpectedError("Either the '--fleet' or the '--config' option must be provided"); } const { validateFilePath } = await Promise.resolve().then(() => require('../../utils/validation')); if (options.config != null) { await validateFilePath(options.config); } } class DeviceInitCmd extends core_1.Command { async run() { var _a; const { flags: options } = await this.parse(DeviceInitCmd); await validateArgsAndOptions(options); const { promisify } = await Promise.resolve().then(() => require('util')); const { promises: fs } = await Promise.resolve().then(() => require('node:fs')); const tmp = await Promise.resolve().then(() => require('tmp')); const tmpNameAsync = promisify(tmp.tmpName); tmp.setGracefulCleanup(); const { downloadOSImage } = await Promise.resolve().then(() => require('../../utils/cloud')); const { getApplication } = await Promise.resolve().then(() => require('../../utils/sdk')); const Logger = await Promise.resolve().then(() => require('../../utils/logger')); const logger = Logger.getLogger(); const balena = (0, lazy_1.getBalenaSdk)(); let fleetSlugOrId = options.fleet; let configJson; if (options.config != null) { const { readAndValidateConfigJson } = await Promise.resolve().then(() => require('../../utils/config')); configJson = await readAndValidateConfigJson(options.config); fleetSlugOrId = configJson.applicationId; } const application = fleetSlugOrId ? await getApplication(balena, fleetSlugOrId, { $select: ['id', 'slug'], $expand: { is_for__device_type: { $select: 'slug', }, }, }) : await (await Promise.resolve().then(() => require('../../utils/patterns'))).selectApplication(); const deviceUuid = balena.models.device.generateUniqueKey(); console.info(`Registering to ${application.slug}: ${deviceUuid}`); const device = await balena.models.device.register(application.id, deviceUuid); const tmpPath = (await tmpNameAsync()); try { logger.logDebug(`Downloading OS image...`); const osVersion = options['os-version'] || 'default'; const deviceType = (_a = configJson === null || configJson === void 0 ? void 0 : configJson.deviceType) !== null && _a !== void 0 ? _a : application.is_for__device_type[0].slug; await downloadOSImage(deviceType, tmpPath, osVersion); logger.logDebug(`Configuring OS image...`); await this.configureOsImage(tmpPath, device, options, configJson, logger); logger.logDebug(`Writing OS image...`); await this.writeOsImage(tmpPath, deviceType, options); } catch (e) { try { logger.logDebug(`Process failed, removing device ${device.uuid}`); await balena.models.device.remove(device.uuid); } catch (_b) { } throw e; } finally { logger.logDebug(`Removing temporary OS image download...`); await fs.rm(tmpPath, { recursive: true, force: true }); } console.log('Done'); return device.uuid; } async configureOsImage(osImagePath, device, options, configJson, logger) { let tmpConfigJsonPath; const { promises: fs } = await Promise.resolve().then(() => require('node:fs')); try { const configureCommand = ['os', 'configure', osImagePath]; if (configJson != null) { const { populateDeviceConfig } = await Promise.resolve().then(() => require('../../utils/config')); populateDeviceConfig(configJson, device, device.api_key); const tmp = await Promise.resolve().then(() => require('tmp')); const { promisify } = await Promise.resolve().then(() => require('util')); const tmpNameAsync = promisify(tmp.tmpName); tmp.setGracefulCleanup(); tmpConfigJsonPath = (await tmpNameAsync()); const fs = await Promise.resolve().then(() => require('fs/promises')); await fs.writeFile(tmpConfigJsonPath, JSON.stringify(configJson)); configureCommand.push('--config', tmpConfigJsonPath); } else { configureCommand.push('--device', device.uuid); if (options.advanced) { configureCommand.push('--advanced'); } if (options['provisioning-key-name']) { configureCommand.push('--provisioning-key-name', options['provisioning-key-name']); } if (options['provisioning-key-expiry-date']) { configureCommand.push('--provisioning-key-expiry-date', options['provisioning-key-expiry-date']); } } await (0, helpers_1.runCommand)(configureCommand); } finally { if (tmpConfigJsonPath != null) { logger.logDebug(`Removing temporary config.json...`); await fs.rm(tmpConfigJsonPath, { recursive: true, force: true }); } } } async writeOsImage(path, deviceType, options) { const osInitCommand = ['os', 'initialize', path, '--type', deviceType]; if (options.yes) { osInitCommand.push('--yes'); } if (options.drive) { osInitCommand.push('--drive', options.drive); } await (0, helpers_1.runCommand)(osInitCommand); } } DeviceInitCmd.description = (0, lazy_1.stripIndent) ` Initialize a device with balenaOS. Register a new device in the selected fleet, download the OS image for the fleet's default device type, configure the image and write it to an SD card. This command effectively combines several other balena CLI commands in one, namely: 'balena device register' 'balena os download' 'balena config generate' 'balena os configure' 'balena os local flash' Possible arguments for the '--fleet', '--os-version' and '--drive' options can be listed respectively with the commands: 'balena fleet list' 'balena os versions' 'balena util available-drives' If the '--fleet' or '--drive' options are omitted, interactive menus will be presented with values to choose from. If the '--os-version' option is omitted, the latest released OS version for the fleet's default device type will be used. ${messages_1.applicationIdInfo.split('\n').join('\n\t\t')} Image configuration questions will be asked interactively unless a pre-configured 'config.json' file is provided with the '--config' option. The file can be generated with the 'balena config generate' command. `; DeviceInitCmd.examples = [ '$ balena device init', '$ balena device init -f myorg/myfleet', '$ balena device init --fleet myFleet --os-version 2.101.7 --drive /dev/disk5', '$ balena device init --fleet myFleet --os-version 2.83.21+rev1.prod --drive /dev/disk5', '$ balena device init --config config.json --os-version 2.101.7 --drive /dev/disk5 --yes', ]; DeviceInitCmd.flags = (() => { const inlineConfiFlags = { advanced: core_1.Flags.boolean({ char: 'v', description: 'show advanced configuration options', }), 'provisioning-key-name': core_1.Flags.string({ description: 'custom key name assigned to generated provisioning api key', }), 'provisioning-key-expiry-date': core_1.Flags.string({ description: 'expiry date assigned to generated provisioning api key (format: YYYY-MM-DD)', }), }; return { fleet: { ...cf.fleet, exclusive: ['config'] }, config: core_1.Flags.string({ description: 'path to the config JSON file, see `balena config generate`', exclusive: ['fleet', ...Object.keys(inlineConfiFlags)], }), 'os-version': core_1.Flags.string({ description: (0, lazy_1.stripIndent) ` exact version number, or a valid semver range, or 'latest' (includes pre-releases), or 'default' (excludes pre-releases if at least one stable version is available), or 'recommended' (excludes pre-releases, will fail if only pre-release versions are available), or 'menu' (will show the interactive menu) `, }), ...inlineConfiFlags, drive: cf.drive, yes: cf.yes, }; })(); DeviceInitCmd.authenticated = true; exports.default = DeviceInitCmd; //# sourceMappingURL=init.js.map