Skip to content

Commit

Permalink
Target hostapp hook: Only patch should_be_operated_by__release if nul…
Browse files Browse the repository at this point in the history
…l or older release

With Supervisor managed HUP, we'll soon start relying on should_be_operated_by__release
to determine the target hostapp release. The Supervisor reports os_version and os_variant
on device provision, and the current target-hostapp hook will set the should_be_operated_by field
when receiving that initial state report. However, SV manage HUP means this field could be set
prior to device provision, so we don't want to overwrite it if it's not null.

If the should_be_operated_by field has a release that's older than the reported OS release
though, we should update the field to the reported OS release.

Change-type: patch
Signed-off-by: Christina Ying Wang <[email protected]>
  • Loading branch information
cywang117 committed Dec 13, 2024
1 parent ad6224f commit 2bde660
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 4 deletions.
20 changes: 16 additions & 4 deletions src/features/hostapp/hooks/target-hostapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ hooks.addPureHook('PATCH', 'resin', 'device', {

/**
* When a device checks in with it's initial OS version, set the corresponding should_be_operated_by__release resource
* using its current reported version.
* using its current reported version only if should_be_operated_by__release is null or older than the current OS version.
* This ensures the target hostapp field isn't overwritten by an older OS release when a device reports OS info on provision.
*/
hooks.addPureHook('PATCH', 'resin', 'device', {
async PRERUN(args) {
Expand Down Expand Up @@ -121,7 +122,7 @@ async function setOSReleaseResource(
id: { $in: deviceIds },
os_version: null,
},
$select: ['id', 'is_of__device_type'],
$select: ['id', 'should_be_operated_by__release', 'is_of__device_type'],
},
});

Expand All @@ -147,8 +148,6 @@ async function setOSReleaseResource(
return Promise.all(
Array.from(devicesByDeviceTypeId.entries()).map(
async ([deviceTypeId, affectedDevices]) => {
const affectedDeviceIds = affectedDevices.map((d) => d.id);

const osRelease = await getOSReleaseResource(
api,
osVersion,
Expand All @@ -160,6 +159,19 @@ async function setOSReleaseResource(
return;
}

// Only patch should_be_operated_by__release if it's null or older than the reported OS release
const affectedDeviceIds = affectedDevices
.filter(
(d) =>
d.should_be_operated_by__release == null ||
d.should_be_operated_by__release.__id < osRelease.id,
)
.map((d) => d.id);

if (affectedDeviceIds.length === 0) {
return;
}

await rootApi.patch({
resource: 'device',
options: {
Expand Down
103 changes: 103 additions & 0 deletions test/15_target-hostapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,109 @@ export default () => {
.expect(200);
expect(dev.d[0].should_be_operated_by__release).to.be.null;
});

it('should overwrite existing should_be_operated_by__release when device reports OS info if should_be_operated_by__release is null', async () => {
// First set up a device with a specific target release
const device = await fakeDevice.provisionDevice(admin, applicationId);
await supertest(admin)
.patch(`/${version}/device(${device.id})`)
.send({
should_be_operated_by__release: null,
})
.expect(200);

// Verify initial state
await expectResourceToMatch(pineUser, 'device', device.id, {
should_be_operated_by__release: null,
});

// Now patch the device with OS info
await device.patchStateV2({
local: {
os_version: 'balenaOS 2.50.0+rev1',
os_variant: 'prod',
},
});

// Verify the should_be_operated_by__release was updated with OS info
await expectResourceToMatch(pineUser, 'device', device.id, {
should_be_operated_by__release: {
__id: nuc2_50_0_rev1prodId,
},
os_version: 'balenaOS 2.50.0+rev1',
os_variant: 'prod',
});
});

it('should not overwrite existing should_be_operated_by__release when device reports OS info if should_be_operated_by__release is newer', async () => {
// First set up a device with a specific target release
const device = await fakeDevice.provisionDevice(admin, applicationId);
await supertest(admin)
.patch(`/${version}/device(${device.id})`)
.send({
should_be_operated_by__release: nuc2_51_0_rev1prodTagAndSemverId,
})
.expect(200);

// Verify initial state
await expectResourceToMatch(pineUser, 'device', device.id, {
should_be_operated_by__release: {
__id: nuc2_51_0_rev1prodTagAndSemverId,
},
});

// Now patch the device with OS info that would normally map to a different & older release
await device.patchStateV2({
local: {
os_version: 'balenaOS 2.50.0+rev1',
os_variant: 'prod',
},
});

// Verify the should_be_operated_by__release was not changed
await expectResourceToMatch(pineUser, 'device', device.id, {
should_be_operated_by__release: {
__id: nuc2_51_0_rev1prodTagAndSemverId,
},
os_version: 'balenaOS 2.50.0+rev1',
os_variant: 'prod',
});
});

it('should overwrite existing should_be_operated_by__release when device reports OS info if should_be_operated_by__release is older', async () => {
// First set up a device with a specific target release
const device = await fakeDevice.provisionDevice(admin, applicationId);
await supertest(admin)
.patch(`/${version}/device(${device.id})`)
.send({
should_be_operated_by__release: unifiedSemverOnlyHostAppReleaseId,
})
.expect(200);

// Verify initial state
await expectResourceToMatch(pineUser, 'device', device.id, {
should_be_operated_by__release: {
__id: unifiedSemverOnlyHostAppReleaseId,
},
});

// Now patch the device with OS info that would normally map to a different & newer release
await device.patchStateV2({
local: {
os_version: 'balenaOS 2.88.5+rev1',
os_variant: 'prod',
},
});

// Verify the should_be_operated_by__release was updated with newer release
await expectResourceToMatch(pineUser, 'device', device.id, {
should_be_operated_by__release: {
__id: unifiedSemverRevHostAppReleaseId,
},
os_version: 'balenaOS 2.88.5+rev1',
os_variant: 'prod',
});
});
});
});
};

0 comments on commit 2bde660

Please sign in to comment.