diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 45480c40a..527451ed3 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -301,8 +301,8 @@ const sdk = fromSharedOptions(); * [.pinToRelease(uuidOrIdOrArray, fullReleaseHashOrId)](#balena.models.device.pinToRelease) ⇒ Promise * [.trackApplicationRelease(uuidOrIdOrArray)](#balena.models.device.trackApplicationRelease) ⇒ Promise * [.setSupervisorRelease(uuidOrIdOrArray, supervisorVersionOrId)](#balena.models.device.setSupervisorRelease) ⇒ Promise - * [.startOsUpdate(uuidOrUuids, targetOsVersion)](#balena.models.device.startOsUpdate) ⇒ Promise - * [.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise + * [.startOsUpdate(uuidOrUuids, targetOsVersion, [options])](#balena.models.device.startOsUpdate) ⇒ Promise + * ~~[.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise~~ * [.ping(uuidOrId)](#balena.models.device.ping) ⇒ Promise * ~~[.getApplicationInfo(uuidOrId)](#balena.models.device.getApplicationInfo) ⇒ Promise~~ * [.identify(uuidOrId)](#balena.models.device.identify) ⇒ Promise @@ -708,8 +708,8 @@ balena.models.device.get(123).catch(function (error) { * [.pinToRelease(uuidOrIdOrArray, fullReleaseHashOrId)](#balena.models.device.pinToRelease) ⇒ Promise * [.trackApplicationRelease(uuidOrIdOrArray)](#balena.models.device.trackApplicationRelease) ⇒ Promise * [.setSupervisorRelease(uuidOrIdOrArray, supervisorVersionOrId)](#balena.models.device.setSupervisorRelease) ⇒ Promise - * [.startOsUpdate(uuidOrUuids, targetOsVersion)](#balena.models.device.startOsUpdate) ⇒ Promise - * [.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise + * [.startOsUpdate(uuidOrUuids, targetOsVersion, [options])](#balena.models.device.startOsUpdate) ⇒ Promise + * ~~[.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise~~ * [.ping(uuidOrId)](#balena.models.device.ping) ⇒ Promise * ~~[.getApplicationInfo(uuidOrId)](#balena.models.device.getApplicationInfo) ⇒ Promise~~ * [.identify(uuidOrId)](#balena.models.device.identify) ⇒ Promise @@ -2283,8 +2283,8 @@ balena.models.application.revokeSupportAccess(123); * [.pinToRelease(uuidOrIdOrArray, fullReleaseHashOrId)](#balena.models.device.pinToRelease) ⇒ Promise * [.trackApplicationRelease(uuidOrIdOrArray)](#balena.models.device.trackApplicationRelease) ⇒ Promise * [.setSupervisorRelease(uuidOrIdOrArray, supervisorVersionOrId)](#balena.models.device.setSupervisorRelease) ⇒ Promise - * [.startOsUpdate(uuidOrUuids, targetOsVersion)](#balena.models.device.startOsUpdate) ⇒ Promise - * [.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise + * [.startOsUpdate(uuidOrUuids, targetOsVersion, [options])](#balena.models.device.startOsUpdate) ⇒ Promise + * ~~[.getOsUpdateStatus(uuid)](#balena.models.device.getOsUpdateStatus) ⇒ Promise~~ * [.ping(uuidOrId)](#balena.models.device.ping) ⇒ Promise * ~~[.getApplicationInfo(uuidOrId)](#balena.models.device.getApplicationInfo) ⇒ Promise~~ * [.identify(uuidOrId)](#balena.models.device.identify) ⇒ Promise @@ -4017,7 +4017,7 @@ balena.models.device.setSupervisorRelease(123, '11.4.14').then(function() { ``` -##### device.startOsUpdate(uuidOrUuids, targetOsVersion) ⇒ Promise +##### device.startOsUpdate(uuidOrUuids, targetOsVersion, [options]) ⇒ Promise **Kind**: static method of [device](#balena.models.device) **Summary**: Start an OS update on a device **Access**: public @@ -4027,6 +4027,8 @@ balena.models.device.setSupervisorRelease(123, '11.4.14').then(function() { | --- | --- | --- | | uuidOrUuids | String \| Array.<String> | full device uuid or array of full uuids | | targetOsVersion | String | semver-compatible version for the target device Unsupported (unpublished) version will result in rejection. The version **must** be the exact version number, a "prod" variant and greater than the one running on the device. To resolve the semver-compatible range use `balena.model.os.getMaxSatisfyingVersion`. | +| [options] | Object | options | +| [options.runDetached] | Boolean | run the update in detached mode. Default behaviour is runDetached=false but is DEPRECATED and will be removed in a future release. Use runDetached=true for more reliable updates. | **Example** ```js @@ -4036,9 +4038,11 @@ balena.models.device.startOsUpdate('7cf02a687b74206f92cb455969cf8e98', '2.29.2+r ``` -##### device.getOsUpdateStatus(uuid) ⇒ Promise +##### ~~device.getOsUpdateStatus(uuid) ⇒ Promise~~ +***Deprecated*** + **Kind**: static method of [device](#balena.models.device) -**Summary**: Get the OS update status of a device +**Summary**: Get the OS update status of a device. This will no longer return a useful status for runDetached=true updates. **Access**: public **Fulfil**: Object - action response diff --git a/src/models/device.ts b/src/models/device.ts index 15c960b7d..2da50a1cd 100644 --- a/src/models/device.ts +++ b/src/models/device.ts @@ -151,8 +151,9 @@ const getDeviceModel = function ( const getOsUpdateHelper = once(async () => { const $deviceUrlsBase = await getDeviceUrlsBase(); - const { getOsUpdateHelper: _getOsUpdateHelper } = - require('../util/device-actions/os-update') as typeof import('../util/device-actions/os-update'); + const _getOsUpdateHelper = ( + await import('../util/device-actions/os-update') + ).getOsUpdateHelper; return _getOsUpdateHelper($deviceUrlsBase, request); }); /* eslint-enable @typescript-eslint/no-require-imports */ @@ -334,14 +335,17 @@ const getDeviceModel = function ( async function startOsUpdate( uuidOrUuids: string, targetOsVersion: string, + options?: { runDetached?: boolean }, ): Promise; async function startOsUpdate( uuidOrUuids: string[], targetOsVersion: string, + options?: { runDetached?: boolean }, ): Promise>; async function startOsUpdate( uuidOrUuids: string | string[], targetOsVersion: string, + options: { runDetached?: boolean } = { runDetached: false }, ): Promise> { if (!targetOsVersion) { throw new errors.BalenaInvalidParameterError( @@ -369,6 +373,7 @@ const getDeviceModel = function ( ); const osUpdateHelper = await getOsUpdateHelper(); + const results: Dictionary< ResolvableReturnType > = {}; @@ -410,10 +415,13 @@ const getDeviceModel = function ( ); } } + + // use the v2 device actions api for detached updates await limitedMap(devices, async (device) => { results[device.uuid] = await osUpdateHelper.startOsUpdate( device.uuid, targetOsVersion, + options.runDetached === true ? 'v2' : 'v1', ); }); }, @@ -2262,6 +2270,10 @@ const getDeviceModel = function ( * Unsupported (unpublished) version will result in rejection. * The version **must** be the exact version number, a "prod" variant and greater than the one running on the device. * To resolve the semver-compatible range use `balena.model.os.getMaxSatisfyingVersion`. + * @param {Object} [options] - options + * @param {Boolean} [options.runDetached] - run the update in detached mode. + * Default behaviour is runDetached=false but is DEPRECATED and will be removed in a future release. Use runDetached=true + * for more reliable updates. * @fulfil {Object} - action response * @returns {Promise} * @@ -2273,7 +2285,8 @@ const getDeviceModel = function ( startOsUpdate, /** - * @summary Get the OS update status of a device + * @deprecated + * @summary Get the OS update status of a device. This will no longer return a useful status for runDetached=true updates. * @name getOsUpdateStatus * @public * @function diff --git a/src/util/device-actions/device-actions-service.ts b/src/util/device-actions/device-actions-service.ts index afaad878e..e50a833d0 100644 --- a/src/util/device-actions/device-actions-service.ts +++ b/src/util/device-actions/device-actions-service.ts @@ -3,6 +3,7 @@ import type { BalenaRequest } from 'balena-request'; interface MakeActionRequestParams { uuid: string; actionNameOrId: string | number; + deviceActionsApiVersion: 'v1' | 'v2'; params?: any; method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; extraOptions: any; @@ -11,6 +12,7 @@ interface MakeActionRequestParams { interface StartActionParams { uuid: string; actionName: string; + deviceActionsApiVersion: 'v1' | 'v2'; params: any; extraOptions?: any; } @@ -21,8 +23,6 @@ interface GetActionStatusParams { extraOptions?: any; } -const DEVICE_ACTIONS_API_VERSION = 'v1'; - export class DeviceActionsService { private actionsEndpoint: string; @@ -30,12 +30,13 @@ export class DeviceActionsService { deviceUrlsBase: string, private request: BalenaRequest, ) { - this.actionsEndpoint = `https://actions.${deviceUrlsBase}/${DEVICE_ACTIONS_API_VERSION}`; + this.actionsEndpoint = `https://actions.${deviceUrlsBase}`; } public startAction = ({ uuid, actionName, + deviceActionsApiVersion, params, extraOptions, }: StartActionParams) => @@ -43,6 +44,7 @@ export class DeviceActionsService { method: 'POST', uuid, actionNameOrId: actionName, + deviceActionsApiVersion, params, extraOptions, }); @@ -55,6 +57,7 @@ export class DeviceActionsService { this.makeActionRequest({ method: 'GET', uuid, + deviceActionsApiVersion: 'v1', actionNameOrId: actionId, extraOptions, }); @@ -63,6 +66,7 @@ export class DeviceActionsService { method, uuid, actionNameOrId, + deviceActionsApiVersion, params, extraOptions, }: MakeActionRequestParams): Promise => { @@ -70,7 +74,7 @@ export class DeviceActionsService { const { body } = await this.request.send({ method, - url: `${this.actionsEndpoint}/${uuid}/${actionNameOrId}`, + url: `${this.actionsEndpoint}/${deviceActionsApiVersion}/${uuid}/${actionNameOrId}`, body: data, ...extraOptions, }); diff --git a/src/util/device-actions/os-update/index.ts b/src/util/device-actions/os-update/index.ts index a3e2636e9..844e74fb0 100644 --- a/src/util/device-actions/os-update/index.ts +++ b/src/util/device-actions/os-update/index.ts @@ -5,7 +5,13 @@ const OS_UPDATE_ACTION_NAME = 'resinhup'; // See: https://github.com/balena-io/resin-proxy/issues/51#issuecomment-274251469 export interface OsUpdateActionResult { - status: 'idle' | 'in_progress' | 'done' | 'error' | 'configuring'; + status: + | 'idle' + | 'in_progress' + | 'done' + | 'error' + | 'configuring' + | 'triggered'; parameters?: { target_version: string; }; @@ -22,10 +28,15 @@ export const getOsUpdateHelper = function ( request, ); - const startOsUpdate = (uuid: string, targetOsVersion: string) => { + const startOsUpdate = ( + uuid: string, + targetOsVersion: string, + deviceActionsApiVersion: 'v1' | 'v2', + ) => { return deviceActionsService.startAction({ uuid, actionName: OS_UPDATE_ACTION_NAME, + deviceActionsApiVersion, params: { target_version: targetOsVersion, },