Performance: reduce calls to kubernetes api-server (#6963)
Docker CI / Docker Build & Push (push) Has been cancelled
Lint / Linting Checks (push) Has been cancelled
Release Drafter / Update Release Draft (push) Has been cancelled
Release Drafter / Auto Label PR (push) Has been cancelled
Tests / vitest (1) (push) Has been cancelled
Tests / vitest (2) (push) Has been cancelled
Tests / vitest (3) (push) Has been cancelled
Tests / vitest (4) (push) Has been cancelled

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
Elie Mouawad
2026-08-07 18:43:11 -07:00
committed by GitHub
co-authored by shamoon
parent f0844f5cf1
commit 1a047d1dfc
2 changed files with 25 additions and 75 deletions
+17 -38
View File
@@ -1,4 +1,4 @@
import { CoreV1Api, CustomObjectsApi } from "@kubernetes/client-node"; import { CustomObjectsApi } from "@kubernetes/client-node";
import { getKubeConfig, getKubernetes, HTTPROUTE_API_GROUP, HTTPROUTE_API_VERSION } from "utils/config/kubernetes"; import { getKubeConfig, getKubernetes, HTTPROUTE_API_GROUP, HTTPROUTE_API_VERSION } from "utils/config/kubernetes";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
@@ -7,49 +7,28 @@ const logger = createLogger("httproute-list");
const kc = getKubeConfig(); const kc = getKubeConfig();
export default async function listHttpRoute() { export default async function listHttpRoute() {
const crd = kc.makeApiClient(CustomObjectsApi);
const core = kc.makeApiClient(CoreV1Api);
const { gateway } = getKubernetes(); const { gateway } = getKubernetes();
let httpRouteList = [];
if (gateway) { if (gateway) {
// httproutes const crd = kc.makeApiClient(CustomObjectsApi);
const getHttpRoutes = async (namespace) =>
crd const httpRoutes = await crd
.listNamespacedCustomObject({ .listClusterCustomObject({
group: HTTPROUTE_API_GROUP, group: HTTPROUTE_API_GROUP,
version: HTTPROUTE_API_VERSION, version: HTTPROUTE_API_VERSION,
namespace, plural: "httproutes",
plural: "httproutes", })
}) .then((response) => {
.then((response) => { return response?.items ?? [];
return response.items; })
})
.catch((error) => {
logger.error("Error getting httproutes: %d %s %s", error.statusCode, error.body, error.response);
logger.debug(error);
return null;
});
// namespaces
const namespaces = await core
.listNamespace()
.then((response) => response.items.map((ns) => ns.metadata.name))
.catch((error) => { .catch((error) => {
logger.error("Error getting namespaces: %d %s %s", error.statusCode, error.body, error.response); logger.error("Error getting httproutes: %d %s %s", error.statusCode, error.body, error.response);
logger.debug(error); logger.debug(error);
return null; return [];
}); });
if (namespaces) { return httpRoutes;
const httpRouteListUnfiltered = await Promise.all(
namespaces.map(async (namespace) => {
const httpRoutes = await getHttpRoutes(namespace);
return httpRoutes;
}),
);
httpRouteList = httpRouteListUnfiltered.flat().filter((httpRoute) => httpRoute);
}
} }
return httpRouteList;
return [];
} }
+8 -37
View File
@@ -3,19 +3,12 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
const { state, getKubernetes, getKubeConfig, logger } = vi.hoisted(() => { const { state, getKubernetes, getKubeConfig, logger } = vi.hoisted(() => {
const state = { const state = {
enabled: true, enabled: true,
namespaces: ["a", "b"], items: [{ metadata: { name: "r1" } }, { metadata: { name: "r2" } }],
routesByNs: {
a: [{ metadata: { name: "r1" } }],
b: [{ metadata: { name: "r2" } }],
},
crd: { crd: {
listNamespacedCustomObject: vi.fn(async ({ namespace }) => ({ items: state.routesByNs[namespace] ?? [] })), listClusterCustomObject: vi.fn(async () => ({ items: state.items })),
},
core: {
listNamespace: vi.fn(async () => ({ items: state.namespaces.map((n) => ({ metadata: { name: n } })) })),
}, },
kc: { kc: {
makeApiClient: vi.fn((Api) => (Api.name === "CoreV1Api" ? state.core : state.crd)), makeApiClient: vi.fn(() => state.crd),
}, },
}; };
@@ -28,7 +21,6 @@ const { state, getKubernetes, getKubeConfig, logger } = vi.hoisted(() => {
}); });
vi.mock("@kubernetes/client-node", () => ({ vi.mock("@kubernetes/client-node", () => ({
CoreV1Api: class CoreV1Api {},
CustomObjectsApi: class CustomObjectsApi {}, CustomObjectsApi: class CustomObjectsApi {},
})); }));
@@ -47,11 +39,7 @@ describe("utils/kubernetes/httproute-list", () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
state.enabled = true; state.enabled = true;
state.namespaces = ["a", "b"]; state.items = [{ metadata: { name: "r1" } }, { metadata: { name: "r2" } }];
state.routesByNs = {
a: [{ metadata: { name: "r1" } }],
b: [{ metadata: { name: "r2" } }],
};
}); });
it("returns an empty list when gateway discovery is disabled", async () => { it("returns an empty list when gateway discovery is disabled", async () => {
@@ -64,19 +52,18 @@ describe("utils/kubernetes/httproute-list", () => {
expect(result).toEqual([]); expect(result).toEqual([]);
}); });
it("lists namespaces and aggregates httproutes", async () => { it("lists httproutes", async () => {
vi.resetModules(); vi.resetModules();
const listHttpRoute = (await import("./httproute-list")).default; const listHttpRoute = (await import("./httproute-list")).default;
const result = await listHttpRoute(); const result = await listHttpRoute();
expect(result.map((r) => r.metadata.name)).toEqual(["r1", "r2"]); expect(result.map((r) => r.metadata.name)).toEqual(["r1", "r2"]);
expect(state.core.listNamespace).toHaveBeenCalled(); expect(state.crd.listClusterCustomObject).toHaveBeenCalled();
expect(state.crd.listNamespacedCustomObject).toHaveBeenCalledTimes(2);
}); });
it("logs and returns [] when namespace listing fails", async () => { it("logs and returns [] when cluster listing fails", async () => {
state.core.listNamespace.mockRejectedValueOnce({ statusCode: 500, body: "boom", response: "resp" }); state.crd.listClusterCustomObject.mockRejectedValueOnce({ statusCode: 500, body: "boom", response: "resp" });
vi.resetModules(); vi.resetModules();
const listHttpRoute = (await import("./httproute-list")).default; const listHttpRoute = (await import("./httproute-list")).default;
@@ -87,20 +74,4 @@ describe("utils/kubernetes/httproute-list", () => {
expect(logger.error).toHaveBeenCalled(); expect(logger.error).toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalled(); expect(logger.debug).toHaveBeenCalled();
}); });
it("skips namespaces whose httproute queries fail", async () => {
state.crd.listNamespacedCustomObject.mockImplementation(async ({ namespace }) => {
if (namespace === "b") throw { statusCode: 500, body: "boom", response: "resp" };
return { items: state.routesByNs[namespace] ?? [] };
});
vi.resetModules();
const listHttpRoute = (await import("./httproute-list")).default;
const result = await listHttpRoute();
expect(result.map((r) => r.metadata.name)).toEqual(["r1"]);
expect(logger.error).toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalled();
});
}); });