Skip to content

Commit 3f5e7e6

Browse files
authored
Poll update status + disable set target on older releases (#2947)
* poll system update status * disable set target for lower versions
1 parent f9854c6 commit 3f5e7e6

File tree

3 files changed

+58
-23
lines changed

3 files changed

+58
-23
lines changed

app/pages/system/UpdatePage.tsx

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
* Copyright Oxide Computer Company
77
*/
88

9+
import { differenceInMinutes } from 'date-fns'
910
import { useMemo } from 'react'
1011
import * as R from 'remeda'
12+
import { lt as semverLt } from 'semver'
1113

1214
import {
1315
Images24Icon,
@@ -46,7 +48,29 @@ import { percentage, round } from '~/util/math'
4648

4749
export const handle = makeCrumb('System Update')
4850

49-
const statusQuery = apiq('systemUpdateStatus', {})
51+
const SEC = 1000 // ms, obviously
52+
const POLL_FAST = 20 * SEC
53+
const POLL_SLOW = 120 * SEC
54+
55+
const statusQuery = apiq(
56+
'systemUpdateStatus',
57+
{},
58+
{
59+
refetchInterval({ state: { data: status } }) {
60+
if (!status) return false // should be impossible due to prefetch
61+
62+
const now = new Date()
63+
const minSinceTargetSet = status.targetRelease
64+
? differenceInMinutes(now, status.targetRelease.timeRequested)
65+
: null
66+
const minSinceLastStepPlanned = differenceInMinutes(now, status.timeLastStepPlanned)
67+
return minSinceLastStepPlanned < 30 ||
68+
(minSinceTargetSet !== null && minSinceTargetSet < 30)
69+
? POLL_FAST
70+
: POLL_SLOW
71+
},
72+
}
73+
)
5074
const reposQuery = apiq('systemUpdateRepositoryList', { query: { limit: ALL_ISH } })
5175

5276
const refreshData = () =>
@@ -175,7 +199,17 @@ export default function UpdatePage() {
175199
<CardBlock.Body>
176200
<ul className="space-y-3">
177201
{repos.items.map((repo) => {
178-
const isTarget = repo.systemVersion === status.targetRelease?.version
202+
const targetVersion = status.targetRelease?.version
203+
const isTarget = repo.systemVersion === targetVersion
204+
// semverLt looks at prerelease meta but not build meta. In prod
205+
// it doesn't matter either way because there will be neither. On
206+
// dogfood it shouldn't matter because the versions will usually
207+
// be the same and with the same prelease meta and only differing
208+
// build meta, so semverLt will return false and we don't disable
209+
// any. Very important this is
210+
const olderThanTarget = targetVersion
211+
? semverLt(repo.systemVersion, targetVersion)
212+
: false
179213
return (
180214
<li
181215
key={repo.hash}
@@ -226,10 +260,13 @@ export default function UpdatePage() {
226260
errorTitle: `Error setting target release to ${repo.systemVersion}`,
227261
})
228262
}}
229-
// TODO: follow API logic, disabling for older releases.
230-
// Or maybe just have the API tell us by adding a field to
231-
// the TufRepo response type.
232-
disabled={isTarget && 'Already set as target'}
263+
disabled={
264+
isTarget
265+
? 'Already set as target'
266+
: olderThanTarget
267+
? 'Cannot set older release as target'
268+
: false
269+
}
233270
/>
234271
</MoreActionsMenu>
235272
</div>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"react-stately": "^3.32.2",
7171
"recharts": "^2.15.1",
7272
"remeda": "^2.30.0",
73+
"semver": "^7.7.3",
7374
"simplebar-react": "^3.2.6",
7475
"ts-pattern": "^5.8.0",
7576
"tslib": "^2.7.0",
@@ -129,7 +130,6 @@
129130
"prettier": "^3.4.2",
130131
"prettier-plugin-tailwindcss": "^0.6.14",
131132
"resize-observer-polyfill": "^1.5.1",
132-
"semver": "^7.7.3",
133133
"tailwindcss": "^4.1.14",
134134
"tsx": "^4.19.2",
135135
"type-fest": "^4.33.0",

test/e2e/system-update.e2e.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ test('Set target release', async ({ page }) => {
5353
await page.getByRole('button', { name: '17.0.0 actions' }).click()
5454
const disabledItem = page.getByRole('menuitem', { name: 'Set as target release' })
5555
await expect(disabledItem).toBeDisabled()
56+
await disabledItem.hover()
57+
await expect(page.getByText('Already set as target')).toBeVisible()
5658
await page.keyboard.press('Escape')
5759

5860
// Upgrade to 18.0.0
@@ -77,6 +79,13 @@ test('Set target release', async ({ page }) => {
7779

7880
const release18 = page.getByRole('listitem').filter({ hasText: '18.0.0' })
7981
await expect(release18.getByText('Target')).toBeVisible()
82+
83+
// Set target on 17 should still be disabled, but now for a different reason
84+
await page.getByRole('button', { name: '17.0.0 actions' }).click()
85+
const setTargetItem = page.getByRole('menuitem', { name: 'Set as target release' })
86+
await expect(setTargetItem).toBeDisabled()
87+
await setTargetItem.hover()
88+
await expect(page.getByText('Cannot set older release as target')).toBeVisible()
8089
})
8190

8291
test('Cannot downgrade to older release', async ({ page }) => {
@@ -88,23 +97,12 @@ test('Cannot downgrade to older release', async ({ page }) => {
8897
const release17 = page.getByRole('listitem').filter({ hasText: '17.0.0' })
8998
await expect(release17.getByText('Target')).toBeVisible()
9099

91-
// Try to downgrade to 16.0.0
100+
// Try to downgrade to 16.0.0 - button should be disabled
92101
await page.getByRole('button', { name: '16.0.0 actions' }).click()
93-
await page.getByRole('menuitem', { name: 'Set as target release' }).click()
94-
95-
const modal = page.getByRole('dialog', { name: 'Confirm set target release' })
96-
await expect(modal).toBeVisible()
97-
await expect(
98-
modal.getByText('Are you sure you want to set 16.0.0 as the target release?')
99-
).toBeVisible()
100-
101-
await page.getByRole('button', { name: 'Confirm' }).click()
102-
103-
// Verify error toast appears
104-
await expectToast(
105-
page,
106-
'Requested target release (16.0.0) must not be older than current target release (17.0.0).'
107-
)
102+
const setTargetItem = page.getByRole('menuitem', { name: 'Set as target release' })
103+
await expect(setTargetItem).toBeDisabled()
104+
await setTargetItem.hover()
105+
await expect(page.getByText('Cannot set older release as target')).toBeVisible()
108106

109107
// Verify the target release has NOT changed - still 17.0.0
110108
await expect(page.getByLabel('Properties table')).toContainText('17.0.0')

0 commit comments

Comments
 (0)