diff --git a/core/frontend/src/views/ExtensionManagerView.vue b/core/frontend/src/views/ExtensionManagerView.vue index e2cfd47df3..204a5ee6b8 100644 --- a/core/frontend/src/views/ExtensionManagerView.vue +++ b/core/frontend/src/views/ExtensionManagerView.vue @@ -212,6 +212,29 @@ const API_URL = '/kraken/v1.0' const notifier = new Notifier(kraken_service) const ansi = new AnsiUp() +// Axios `timeout` is a hard deadline from request start, so large or slow pulls die +// while Docker is still streaming. Cancel only after this long with no new bytes. +const PULL_IDLE_TIMEOUT_MS = 120000 + +function startPullIdleWatchdog() { + const source = axios.CancelToken.source() + let timer: ReturnType | undefined + const stop = () => { + if (timer !== undefined) { + clearTimeout(timer) + timer = undefined + } + } + const bump = () => { + stop() + timer = setTimeout(() => { + source.cancel(`No data received for ${PULL_IDLE_TIMEOUT_MS}ms`) + }, PULL_IDLE_TIMEOUT_MS) + } + bump() + return { cancelToken: source.token, bump, stop } +} + export default Vue.extend({ name: 'ExtensionManagerView', components: { @@ -330,6 +353,7 @@ export default Vue.extend({ notifier.pushBackError('EXTENSIONS_INSTALL_FAIL', error) }, ) + const watchdog = startPullIdleWatchdog() back_axios({ url: `${API_URL}/extension/update_to_version`, method: 'POST', @@ -337,8 +361,9 @@ export default Vue.extend({ extension_identifier: extension.identifier, new_version: version, }, - timeout: 120000, + cancelToken: watchdog.cancelToken, onDownloadProgress: (progressEvent) => { + watchdog.bump() tracker.digestNewData(progressEvent) this.pull_output = tracker.pull_output this.download_percentage = tracker.download_percentage @@ -355,6 +380,7 @@ export default Vue.extend({ notifier.pushBackError('EXTENSIONS_INSTALL_FAIL', error) }) .finally(() => { + watchdog.stop() this.show_pull_output = false this.show_dialog = false this.pull_output = '' @@ -549,6 +575,7 @@ export default Vue.extend({ }, ) + const watchdog = startPullIdleWatchdog() back_axios({ url: `${API_URL}/extension/install`, method: 'POST', @@ -561,7 +588,9 @@ export default Vue.extend({ permissions, user_permissions, }, + cancelToken: watchdog.cancelToken, onDownloadProgress: (progressEvent) => { + watchdog.bump() tracker.digestNewData(progressEvent) this.pull_output = tracker.pull_output this.download_percentage = tracker.download_percentage @@ -578,6 +607,7 @@ export default Vue.extend({ notifier.pushBackError('EXTENSIONS_INSTALL_FAIL', error) }) .finally(() => { + watchdog.stop() this.show_pull_output = false this.show_dialog = false this.pull_output = ''