From 66575982afb9c08962e48bd768ff83f0c13c003b Mon Sep 17 00:00:00 2001 From: Gandalf the Greybeard Date: Wed, 25 Mar 2026 05:57:15 +0000 Subject: [PATCH] fix: add request timeout wrapper to prevent E2E test hang Add withTimeout() helper that wraps ApiProxy.request calls with a 2s timeout. This prevents the plugin from hanging indefinitely when CRD requests fail or network issues occur in the E2E environment. Root cause: ApiProxy.request to non-existent CRDs would hang forever, causing the Loading Intel GPU data... progressbar to never resolve. Co-Authored-By: Paperclip --- src/api/IntelGpuDataContext.tsx | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/api/IntelGpuDataContext.tsx b/src/api/IntelGpuDataContext.tsx index bf724a9..6ae9a4c 100644 --- a/src/api/IntelGpuDataContext.tsx +++ b/src/api/IntelGpuDataContext.tsx @@ -69,6 +69,18 @@ export function useIntelGpuContext(): IntelGpuContextValue { // Helpers // --------------------------------------------------------------------------- +const DEFAULT_REQUEST_TIMEOUT_MS = 2_000; + +/** Wraps a promise with a timeout, rejecting if it doesn't settle within ms. */ +function withTimeout(promise: Promise, ms: number): Promise { + return Promise.race([ + promise, + new Promise((_, reject) => + setTimeout(() => reject(new Error(`Request timed out after ${ms}ms`)), ms) + ), + ]); +} + /** Extract raw Kubernetes JSON from Headlamp KubeObject wrappers. */ const extractJsonData = (items: unknown[]): unknown[] => items.map(item => @@ -108,8 +120,11 @@ export function IntelGpuDataProvider({ children }: { children: React.ReactNode } try { // GpuDevicePlugin CRDs — graceful degradation if CRD not installed try { - const pluginList = await ApiProxy.request( - `/apis/${INTEL_DEVICE_PLUGIN_API_GROUP}/${INTEL_DEVICE_PLUGIN_API_VERSION}/gpudeviceplugins` + const pluginList = await withTimeout( + ApiProxy.request( + `/apis/${INTEL_DEVICE_PLUGIN_API_GROUP}/${INTEL_DEVICE_PLUGIN_API_VERSION}/gpudeviceplugins` + ), + DEFAULT_REQUEST_TIMEOUT_MS ); if (!cancelled && isKubeList(pluginList)) { setCrdAvailable(true); @@ -139,7 +154,10 @@ export function IntelGpuDataProvider({ children }: { children: React.ReactNode } for (const url of pluginPodSelectors) { try { - const list = await ApiProxy.request(url); + const list = await withTimeout( + ApiProxy.request(url), + DEFAULT_REQUEST_TIMEOUT_MS + ); if (!cancelled && isKubeList(list)) { const gpuPluginPods = filterIntelGpuPluginPods(list.items); foundPluginPods.push(...gpuPluginPods);