1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix getLatestApiPrefixGroup to not fail when options.apiBase is not provided (#4462)

This commit is contained in:
Sebastian Malton 2021-11-30 11:27:20 -05:00 committed by GitHub
parent 37b8fe45f5
commit 29220c1c73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 40 deletions

View File

@ -25,6 +25,7 @@ import { KubeJsonApi } from "../kube-json-api";
import { KubeObject } from "../kube-object"; import { KubeObject } from "../kube-object";
import AbortController from "abort-controller"; import AbortController from "abort-controller";
import { delay } from "../../utils/delay"; import { delay } from "../../utils/delay";
import { PassThrough } from "stream";
class TestKubeObject extends KubeObject { class TestKubeObject extends KubeObject {
static kind = "Pod"; static kind = "Pod";
@ -170,8 +171,7 @@ describe("KubeApi", () => {
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses"; const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
const kubeApi = new KubeApi({ const kubeApi = new KubeApi({
request, request,
objectConstructor: KubeObject, objectConstructor: Object.assign(KubeObject, { apiBase }),
apiBase,
fallbackApiBases: [fallbackApiBase], fallbackApiBases: [fallbackApiBase],
checkPreferredVersion: true, checkPreferredVersion: true,
}); });
@ -304,19 +304,28 @@ describe("KubeApi", () => {
describe("watch", () => { describe("watch", () => {
let api: TestKubeApi; let api: TestKubeApi;
let stream: PassThrough;
beforeEach(() => { beforeEach(() => {
api = new TestKubeApi({ api = new TestKubeApi({
request, request,
objectConstructor: TestKubeObject, objectConstructor: TestKubeObject,
}); });
stream = new PassThrough();
});
afterEach(() => {
stream.end();
stream.destroy();
}); });
it("sends a valid watch request", () => { it("sends a valid watch request", () => {
const spy = jest.spyOn(request, "getResponse"); const spy = jest.spyOn(request, "getResponse");
(fetch as any).mockResponse(async () => { (fetch as any).mockResponse(async () => {
return {}; return {
body: stream,
};
}); });
api.watch({ namespace: "kube-system" }); api.watch({ namespace: "kube-system" });
@ -327,7 +336,9 @@ describe("KubeApi", () => {
const spy = jest.spyOn(request, "getResponse"); const spy = jest.spyOn(request, "getResponse");
(fetch as any).mockResponse(async () => { (fetch as any).mockResponse(async () => {
return {}; return {
body: stream,
};
}); });
api.watch({ namespace: "kube-system", timeout: 60 }); api.watch({ namespace: "kube-system", timeout: 60 });
@ -342,11 +353,13 @@ describe("KubeApi", () => {
done(); done();
}); });
return {}; return {
body: stream,
};
}); });
const abortController = new AbortController(); const abortController = new AbortController();
api.watch({ api.watch({
namespace: "kube-system", namespace: "kube-system",
timeout: 60, timeout: 60,
@ -364,30 +377,32 @@ describe("KubeApi", () => {
it("if request ended", (done) => { it("if request ended", (done) => {
const spy = jest.spyOn(request, "getResponse"); const spy = jest.spyOn(request, "getResponse");
jest.spyOn(stream, "on").mockImplementation((eventName: string, callback: Function) => {
// End the request in 100ms.
if (eventName === "end") {
setTimeout(() => {
callback();
}, 100);
}
return stream;
});
// we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely // we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely
jest.spyOn(global, "fetch").mockImplementation(async () => { jest.spyOn(global, "fetch").mockImplementation(async () => {
return { return {
ok: true, ok: true,
body: { body: stream,
on: (eventName: string, callback: Function) => {
// End the request in 100ms.
if (eventName === "end") {
setTimeout(() => {
callback();
}, 100);
}
},
},
} as any; } as any;
}); });
api.watch({ api.watch({
namespace: "kube-system", namespace: "kube-system",
}); });
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1);
setTimeout(() => { setTimeout(() => {
expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledTimes(2);
done(); done();
}, 2000); }, 2000);
@ -397,11 +412,13 @@ describe("KubeApi", () => {
const spy = jest.spyOn(request, "getResponse"); const spy = jest.spyOn(request, "getResponse");
(fetch as any).mockResponse(async () => { (fetch as any).mockResponse(async () => {
return {}; return {
body: stream,
};
}); });
const timeoutSeconds = 1; const timeoutSeconds = 1;
api.watch({ api.watch({
namespace: "kube-system", namespace: "kube-system",
timeout: timeoutSeconds, timeout: timeoutSeconds,
@ -409,7 +426,7 @@ describe("KubeApi", () => {
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1);
setTimeout(() => { setTimeout(() => {
expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledTimes(2);
done(); done();
}, timeoutSeconds * 1000 * 1.2); }, timeoutSeconds * 1000 * 1.2);
@ -418,25 +435,27 @@ describe("KubeApi", () => {
it("retries only once if request ends and timeout is set", (done) => { it("retries only once if request ends and timeout is set", (done) => {
const spy = jest.spyOn(request, "getResponse"); const spy = jest.spyOn(request, "getResponse");
jest.spyOn(stream, "on").mockImplementation((eventName: string, callback: Function) => {
// End the request in 100ms.
if (eventName === "end") {
setTimeout(() => {
callback();
}, 100);
}
return stream;
});
// we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely // we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely
jest.spyOn(global, "fetch").mockImplementation(async () => { jest.spyOn(global, "fetch").mockImplementation(async () => {
return { return {
ok: true, ok: true,
body: { body: stream,
on: (eventName: string, callback: Function) => {
// End the request in 100ms
if (eventName === "end") {
setTimeout(() => {
callback();
}, 100);
}
},
},
} as any; } as any;
}); });
const timeoutSeconds = 0.5; const timeoutSeconds = 0.5;
api.watch({ api.watch({
namespace: "kube-system", namespace: "kube-system",
timeout: timeoutSeconds, timeout: timeoutSeconds,
@ -444,7 +463,7 @@ describe("KubeApi", () => {
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1);
setTimeout(() => { setTimeout(() => {
expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledTimes(2);
done(); done();
}, 2000); }, 2000);

View File

@ -295,14 +295,18 @@ export class KubeApi<T extends KubeObject> {
*/ */
private async getLatestApiPrefixGroup() { private async getLatestApiPrefixGroup() {
// Note that this.options.apiBase is the "full" url, whereas this.apiBase is parsed // Note that this.options.apiBase is the "full" url, whereas this.apiBase is parsed
const apiBases = [this.options.apiBase, ...this.options.fallbackApiBases]; const apiBases = [this.options.apiBase, this.objectConstructor.apiBase, ...this.options.fallbackApiBases];
for (const apiUrl of apiBases) { for (const apiUrl of apiBases) {
// Split e.g. "/apis/extensions/v1beta1/ingresses" to parts if (!apiUrl) {
const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl); continue;
}
// Request available resources
try { try {
// Split e.g. "/apis/extensions/v1beta1/ingresses" to parts
const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl);
// Request available resources
const response = await this.request.get<IKubeResourceList>(`${apiPrefix}/${apiVersionWithGroup}`); const response = await this.request.get<IKubeResourceList>(`${apiPrefix}/${apiVersionWithGroup}`);
// If the resource is found in the group, use this apiUrl // If the resource is found in the group, use this apiUrl
@ -326,7 +330,7 @@ export class KubeApi<T extends KubeObject> {
return await this.getLatestApiPrefixGroup(); return await this.getLatestApiPrefixGroup();
} catch (error) { } catch (error) {
// If valid API wasn't found, log the error and return defaults below // If valid API wasn't found, log the error and return defaults below
logger.error(error); logger.error(`[KUBE-API]: ${error}`);
} }
} }

View File

@ -219,7 +219,7 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
case "rejected": case "rejected":
if (onLoadFailure) { if (onLoadFailure) {
onLoadFailure(result.reason.message); onLoadFailure(result.reason.message || result.reason);
} else { } else {
// if onLoadFailure is not provided then preserve old behaviour // if onLoadFailure is not provided then preserve old behaviour
throw result.reason; throw result.reason;