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,
},