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
parent 45910043d7
commit fd84faf6ce
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,7 +353,9 @@ describe("KubeApi", () => {
done(); done();
}); });
return {}; return {
body: stream,
};
}); });
const abortController = new AbortController(); const abortController = new AbortController();
@ -364,20 +377,22 @@ describe("KubeApi", () => {
it("if request ended", (done) => { it("if request ended", (done) => {
const spy = jest.spyOn(request, "getResponse"); const spy = jest.spyOn(request, "getResponse");
// we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely jest.spyOn(stream, "on").mockImplementation((eventName: string, callback: Function) => {
jest.spyOn(global, "fetch").mockImplementation(async () => {
return {
ok: true,
body: {
on: (eventName: string, callback: Function) => {
// End the request in 100ms. // End the request in 100ms.
if (eventName === "end") { if (eventName === "end") {
setTimeout(() => { setTimeout(() => {
callback(); callback();
}, 100); }, 100);
} }
},
}, return stream;
});
// we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely
jest.spyOn(global, "fetch").mockImplementation(async () => {
return {
ok: true,
body: stream,
} as any; } as any;
}); });
@ -397,7 +412,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,
};
}); });
const timeoutSeconds = 1; const timeoutSeconds = 1;
@ -418,20 +435,22 @@ 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");
// we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely jest.spyOn(stream, "on").mockImplementation((eventName: string, callback: Function) => {
jest.spyOn(global, "fetch").mockImplementation(async () => { // End the request in 100ms.
return {
ok: true,
body: {
on: (eventName: string, callback: Function) => {
// End the request in 100ms
if (eventName === "end") { if (eventName === "end") {
setTimeout(() => { setTimeout(() => {
callback(); callback();
}, 100); }, 100);
} }
},
}, return stream;
});
// we need to mock using jest as jest-fetch-mock doesn't support mocking the body completely
jest.spyOn(global, "fetch").mockImplementation(async () => {
return {
ok: true,
body: stream,
} as any; } as any;
}); });

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) {
if (!apiUrl) {
continue;
}
try {
// Split e.g. "/apis/extensions/v1beta1/ingresses" to parts // Split e.g. "/apis/extensions/v1beta1/ingresses" to parts
const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl); const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl);
// Request available resources // Request available resources
try {
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;