Skip to content

Commit

Permalink
Pin to target OS release in startOsUpdate & skip action-based HUP if …
Browse files Browse the repository at this point in the history
…target OS version >=vTODO

See: https://balena.fibery.io/Work/Improvement/API-and-SDK-changes-for-supervisor-hostOS-updates-2348
Change-type: minor
Signed-off-by: Christina Ying Wang <[email protected]>
  • Loading branch information
cywang117 committed Dec 3, 2024
1 parent 03bada6 commit 101f190
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 33 deletions.
124 changes: 92 additions & 32 deletions src/models/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export type DeviceMetrics = Pick<
>;

const DEFAULT_DAYS_OF_REQUESTED_HISTORY = 7;
const SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION = 'vTODO';
const SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION_ESR = 'vTODO-ESR';

const getDeviceModel = function (
deps: InjectedDependenciesParam,
Expand Down Expand Up @@ -392,8 +394,12 @@ const getDeviceModel = function (
groupByNavigationPoperty: 'is_of__device_type',
fn: async (devices, deviceTypeId) => {
const dt = await getDeviceType(deviceTypeId);
const toPinOSRelease = new Map<string, number>();
for (const device of devices) {
// this will throw an error if the action isn't available
// Validate current device type, OS version, and OS variant.
// Ensure target OS version is newer than current OS version.
// Also ensure device is online if we're using the actions
// proxy which requires a connection to the device.
exports._checkOsUpdateTarget(
{
...device,
Expand All @@ -402,27 +408,65 @@ const getDeviceModel = function (
targetOsVersion,
);

// Validate target OS version is available for the device type.
const osVersions = await getAvailableOsVersions(dt.slug, isDraft);

if (
!osVersions.some(
(v) => bSemver.compare(v.raw_version, targetOsVersion) === 0,
)
) {
// Find target OS release
const targetRelease = osVersions.find(
(v) => bSemver.compare(v.raw_version, targetOsVersion) === 0,
);

if (!targetRelease) {
throw new errors.BalenaInvalidParameterError(
'targetOsVersion',
targetOsVersion,
);
}

// Mark device to skip action proxy based HUP
// if target OS version >= SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION
if (
// Non-ESR comparison
bSemver.compare(
targetOsVersion,
SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION,
) >= 0 ||
// ESR comparison
bSemver.compare(
targetOsVersion,
SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION_ESR,
) >= 0
) {
toPinOSRelease.set(device.uuid, targetRelease.id);
}
}

// use the v2 device actions api for detached updates
// Trigger OS update via actions proxy, or pin device to target OS release
// if current OS version >= SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION
await limitedMap(devices, async (device) => {
results[device.uuid] = await osUpdateHelper.startOsUpdate(
device.uuid,
targetOsVersion,
options.runDetached === true ? 'v2' : 'v1',
);
const targetReleaseId = toPinOSRelease.get(device.uuid);
if (targetReleaseId) {
// Pin device to target OS release and let Supervisor manage HUP
await sdkInstance.pine.patch({
resource: 'device',
id: device.id,
body: {
should_be_operated_by__release: targetReleaseId,
},
});

results[device.uuid] = {
status: 'pinned',
};
} else {
// Use actions proxy for HUP
results[device.uuid] = await osUpdateHelper.startOsUpdate(
device.uuid,
targetOsVersion,
// use the v2 device actions api for detached updates
options.runDetached === true ? 'v2' : 'v1',
);
}
});
},
});
Expand Down Expand Up @@ -2199,7 +2243,7 @@ const getDeviceModel = function (
* @param {Object} device - A device object
* @param {String} targetOsVersion - semver-compatible version for the target device
* @throws Exception if update isn't supported
* @returns {void}
* @returns {String} current OS version in semver format
*/
_checkOsUpdateTarget(
{
Expand All @@ -2212,32 +2256,28 @@ const getDeviceModel = function (
is_of__device_type: [Pick<DeviceType, 'slug'>];
},
targetOsVersion: string,
) {
): string {
if (!uuid) {
throw new Error('The uuid of the device is not available');
}

if (!is_online) {
throw new Error(`The device is offline: ${uuid}`);
}

if (!os_version) {
throw new Error(
`The current os version of the device is not available: ${uuid}`,
);
}

const deviceType = is_of__device_type?.[0]?.slug;
if (!deviceType) {
// Error if the property is missing
if (os_variant === undefined) {
throw new Error(
`The device type of the device is not available: ${uuid}`,
`The os variant of the device is not available: ${uuid}`,
);
}

// error the property is missing
if (os_variant === undefined) {
const deviceType = is_of__device_type?.[0]?.slug;
if (!deviceType) {
throw new Error(
`The os variant of the device is not available: ${uuid}`,
`The device type of the device is not available: ${uuid}`,
);
}

Expand All @@ -2247,15 +2287,35 @@ const getDeviceModel = function (
os_variant,
}) ?? os_version;

// if the os_version couldn't be parsed
// rely on getHUPActionType to throw an error
// Validate target OS version is newer than current OS version
if (bSemver.compare(currentOsVersion, targetOsVersion) >= 0) {
throw new Error('OS downgrades are not allowed');
}

// this will throw an error if the action isn't available
hupActionHelper().getHUPActionType(
deviceType,
currentOsVersion,
targetOsVersion,
);
// If the os_version couldn't be parsed, rely on getHUPActionType to
// throw an error if <SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION.
// Otherwise, just continue as as we should allow pinning from an
// invalid current OS version to a valid target OS version (assuming
// target OS version is valid).
if (
!bSemver.gte(currentOsVersion, SUPERVISOR_MANAGED_HUP_MIN_OS_VERSION)
) {
// It only matters that the device is online if we're
// HUP-ing using the actions proxy which requires a
// connection to the device.
if (!is_online) {
throw new Error(`The device is offline: ${uuid}`);
}

// This will throw an error if the action isn't available
hupActionHelper().getHUPActionType(
deviceType,
currentOsVersion,
targetOsVersion,
);
}

return currentOsVersion;
},

/**
Expand Down
4 changes: 3 additions & 1 deletion src/util/device-actions/os-update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export interface OsUpdateActionResult {
| 'done'
| 'error'
| 'configuring'
| 'triggered';
| 'triggered'
// Only used for OS >= vTODO which supports Supervisor managed HUP.
| 'pinned';
parameters?: {
target_version: string;
};
Expand Down

0 comments on commit 101f190

Please sign in to comment.