()
| 16033 | |
| 16034 | setTimeout(() => { |
| 16035 | verifyServiceRestart(data.to_commit || ''); |
| 16036 | }, 5000); |
| 16037 | |
| 16038 | } catch (error) { |
| 16039 | console.error('Update error:', error); |
| 16040 | if (isLikelyNetworkError(error)) { |
| 16041 | // The service may have restarted before the response reached us - |
| 16042 | // verify the outcome instead of declaring failure. |
| 16043 | addConsoleMessage('Connection dropped while updating - verifying whether the update completed...', 'info'); |
| 16044 | updateElement('update-info', 'Connection dropped. Verifying update...'); |
| 16045 | setButtonState(true, 'Verifying...'); |
| 16046 | setTimeout(() => { |
| 16047 | verifyServiceRestart(''); |
| 16048 | }, 5000); |
| 16049 | return; |
| 16050 | } |
| 16051 | const payload = error.payload || { error: error.message }; |
| 16052 | if (payload.code === 'busy') { |
| 16053 | // Two tabs, two clicks. The other one is doing the work - follow it |
| 16054 | // rather than reporting a failure that did not happen. |
| 16055 | addConsoleMessage('An update is already running on this box - watching it instead.', 'info'); |
| 16056 | setButtonState(true, 'Updating...'); |
| 16057 | verifyServiceRestart(''); |
| 16058 | return; |
| 16059 | } |
| 16060 | const message = reportUpdateProblem('Update failed', payload); |
| 16061 | setButtonState(false, idleLabel); |
| 16062 | updateElement('update-info', payload.hint || `Update failed: ${message}`); |
| 16063 | } |
| 16064 | } |
| 16065 | |
| 16066 | // Watch the box through the restart. Ragnar hands the tail of an update |
| 16067 | // (dependency installs, provisioning) to a transient systemd unit that restarts |
| 16068 | // the service when it is done, so "the API answers" is not the same as "the |
| 16069 | // update finished" - the commit the box reports has to change too. |
| 16070 | async function verifyServiceRestart(expectedCommit) { |
| 16071 | let attempts = 0; |
| 16072 | // 5 minutes of silence is the giving-up point, but post-update work that is |
| 16073 | // demonstrably still running extends it: a dependency install or an |
| 16074 | // apt-get update on a Pi routinely outlasts five minutes, and reporting |
| 16075 | // "could not confirm the update" over a step that is plainly making |
| 16076 | // progress is a false alarm about a box that is perfectly fine. |
| 16077 | let maxAttempts = 60; |
| 16078 | const hardCeiling = 360; // 30 minutes, even with progress |
| 16079 | let sawRestart = false; |
| 16080 | let lastStep = ''; |
| 16081 | // The service reports when this process started. A change in that value is |
| 16082 | // proof the restart happened, which is more reliable than hoping to catch |
| 16083 | // the box unreachable between two five-second polls. |
| 16084 | let startedAt = null; |
| 16085 | |
| 16086 | addConsoleMessage('Verifying the update landed and the service is back...', 'info'); |
| 16087 | updateElement('update-info', 'Verifying update...'); |
| 16088 | |
| 16089 | const finish = (message, level, info) => { |
| 16090 | addConsoleMessage(message, level); |
| 16091 | updateElement('update-info', info); |
| 16092 | const updateBtn = document.getElementById('update-btn'); |
no test coverage detected