diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index 7ca2995906..6e3ad4a72a 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -403,10 +403,11 @@ export class KubeApi< /** * This method differs from {@link formatUrlForNotListing} because this treats `""` as "all namespaces" + * NOTE: This is also useful for watching * @param namespace The namespace to list in or `""` for all namespaces */ - formatUrlForListing(namespace: string) { - return createKubeApiURL({ + formatUrlForListing(namespace: string | undefined, query?: Partial) { + const resourcePath = createKubeApiURL({ apiPrefix: this.apiPrefix, apiVersion: this.apiVersionWithGroup, resource: this.apiResource, @@ -414,15 +415,15 @@ export class KubeApi< ? namespace ?? "default" : undefined, }); + + return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : ""); } /** * Format a URL pathname and query for acting upon a specific resource. */ - formatUrlForNotListing(resource?: Partial, query?: Partial): string; - - formatUrlForNotListing({ name, namespace }: Partial = {}, query?: Partial) { - const resourcePath = createKubeApiURL({ + formatUrlForNotListing({ name, namespace }: Partial = {}) { + return createKubeApiURL({ apiPrefix: this.apiPrefix, apiVersion: this.apiVersionWithGroup, resource: this.apiResource, @@ -431,15 +432,17 @@ export class KubeApi< : undefined, name, }); - - return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : ""); } /** - * @deprecated use {@link formatUrlForNotListing} instead + * @deprecated use {@link formatUrlForNotListing} or {@link formatUrlForListing} instead */ getUrl(resource?: Partial, query?: Partial) { - return this.formatUrlForNotListing(resource, query); + if (query) { + return this.formatUrlForListing(resource?.namespace, query); + } + + return this.formatUrlForNotListing(resource); } protected normalizeQuery(query: Partial = {}) { @@ -625,14 +628,14 @@ export class KubeApi< } getWatchUrl(namespace?: string, query: KubeApiQueryParams = {}) { - return this.formatUrlForNotListing({ namespace }, { + return this.formatUrlForListing(namespace, { watch: 1, resourceVersion: this.getResourceVersion(namespace), ...query, }); } - watch(opts?: KubeApiWatchOptions): () => void { + watch(opts?: KubeApiWatchOptions): Disposer { let errorReceived = false; let timedRetry: NodeJS.Timeout; const { diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index a82590d758..9e7c541b58 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -412,7 +412,7 @@ export abstract class KubeObjectStore< if (this.api.isNamespaced) { void (async () => { try { - const [loadedNamespaces] = await Promise.race([ + const loadedNamespaces = await Promise.race([ rejectPromiseBy(abortController.signal), waitUntilDefined(() => this.loadedNamespaces.get()), ]); @@ -424,8 +424,8 @@ export abstract class KubeObjectStore< this.watchNamespace(namespace, abortController, { onLoadFailure }); } } - } catch { - // ignore + } catch (error) { + console.error(`[KUBE-OBJECT-STORE]: failed to subscribe to ${this.api.apiBase}`, error); } })(); } else { @@ -441,7 +441,7 @@ export abstract class KubeObjectStore< } let timedRetry: NodeJS.Timeout; - const watch = () => this.api.watch({ + const startNewWatch = () => this.api.watch({ namespace, abortController, callback, @@ -460,7 +460,7 @@ export abstract class KubeObjectStore< // not sure what to do, best to retry clearTimeout(timedRetry); - timedRetry = setTimeout(watch, 5000); + timedRetry = setTimeout(startNewWatch, 5000); } else if (error instanceof KubeStatus && error.code === 410) { clearTimeout(timedRetry); // resourceVersion has gone, let's try to reload @@ -469,11 +469,11 @@ export abstract class KubeObjectStore< namespace ? this.loadAll({ namespaces: [namespace], reqInit: { signal }, ...opts }) : this.loadAll({ merge: false, reqInit: { signal }, ...opts }) - ).then(watch); + ).then(startNewWatch); }, 1000); } else if (error) { // not sure what to do, best to retry clearTimeout(timedRetry); - timedRetry = setTimeout(watch, 5000); + timedRetry = setTimeout(startNewWatch, 5000); } if (data) { @@ -482,7 +482,7 @@ export abstract class KubeObjectStore< }; signal.addEventListener("abort", () => clearTimeout(timedRetry)); - watch(); + startNewWatch(); } @action diff --git a/src/common/utils/wait.ts b/src/common/utils/wait.ts index 7bcbd1688b..402d556b5d 100644 --- a/src/common/utils/wait.ts +++ b/src/common/utils/wait.ts @@ -8,26 +8,23 @@ import type { Disposer } from "./disposer"; export async function waitUntilDefined(getter: (() => T | null | undefined) | IComputedValue, opts?: { timeout?: number }): Promise { return new Promise((resolve, reject) => { - let res: T | null | undefined; - when( () => { - res = typeof getter === "function" + const res = typeof getter === "function" ? getter() : getter.get(); + const isDefined = res != null; - if (res != null) { + if (isDefined) { resolve(res); - - return true; } - return false; + return isDefined; }, () => {}, { onError: reject, - ...opts, + ...(opts ?? {}), }, ); });