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 createLogger from "utils/logger";
@@ -7,49 +7,28 @@ const logger = createLogger("httproute-list");
const kc = getKubeConfig();
export default async function listHttpRoute() {
const crd = kc.makeApiClient(CustomObjectsApi);
const core = kc.makeApiClient(CoreV1Api);
const { gateway } = getKubernetes();
let httpRouteList = [];
if (gateway) {
// httproutes
const getHttpRoutes = async (namespace) =>
crd
.listNamespacedCustomObject({
group: HTTPROUTE_API_GROUP,
version: HTTPROUTE_API_VERSION,
namespace,
plural: "httproutes",
})
.then((response) => {
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))
const crd = kc.makeApiClient(CustomObjectsApi);
const httpRoutes = await crd
.listClusterCustomObject({
group: HTTPROUTE_API_GROUP,
version: HTTPROUTE_API_VERSION,
plural: "httproutes",
})
.then((response) => {
return response?.items ?? [];
})
.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);
return null;
return [];
});
if (namespaces) {
const httpRouteListUnfiltered = await Promise.all(
namespaces.map(async (namespace) => {
const httpRoutes = await getHttpRoutes(namespace);
return httpRoutes;
}),
);
httpRouteList = httpRouteListUnfiltered.flat().filter((httpRoute) => httpRoute);
}
return httpRoutes;
}
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 = {
enabled: true,
namespaces: ["a", "b"],
routesByNs: {
a: [{ metadata: { name: "r1" } }],
b: [{ metadata: { name: "r2" } }],
},
items: [{ metadata: { name: "r1" } }, { metadata: { name: "r2" } }],
crd: {
listNamespacedCustomObject: vi.fn(async ({ namespace }) => ({ items: state.routesByNs[namespace] ?? [] })),
},
core: {
listNamespace: vi.fn(async () => ({ items: state.namespaces.map((n) => ({ metadata: { name: n } })) })),
listClusterCustomObject: vi.fn(async () => ({ items: state.items })),
},
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", () => ({
CoreV1Api: class CoreV1Api {},
CustomObjectsApi: class CustomObjectsApi {},
}));
@@ -47,11 +39,7 @@ describe("utils/kubernetes/httproute-list", () => {
beforeEach(() => {
vi.clearAllMocks();
state.enabled = true;
state.namespaces = ["a", "b"];
state.routesByNs = {
a: [{ metadata: { name: "r1" } }],
b: [{ metadata: { name: "r2" } }],
};
state.items = [{ metadata: { name: "r1" } }, { metadata: { name: "r2" } }];
});
it("returns an empty list when gateway discovery is disabled", async () => {
@@ -64,19 +52,18 @@ describe("utils/kubernetes/httproute-list", () => {
expect(result).toEqual([]);
});
it("lists namespaces and aggregates httproutes", async () => {
it("lists httproutes", async () => {
vi.resetModules();
const listHttpRoute = (await import("./httproute-list")).default;
const result = await listHttpRoute();
expect(result.map((r) => r.metadata.name)).toEqual(["r1", "r2"]);
expect(state.core.listNamespace).toHaveBeenCalled();
expect(state.crd.listNamespacedCustomObject).toHaveBeenCalledTimes(2);
expect(state.crd.listClusterCustomObject).toHaveBeenCalled();
});
it("logs and returns [] when namespace listing fails", async () => {
state.core.listNamespace.mockRejectedValueOnce({ statusCode: 500, body: "boom", response: "resp" });
it("logs and returns [] when cluster listing fails", async () => {
state.crd.listClusterCustomObject.mockRejectedValueOnce({ statusCode: 500, body: "boom", response: "resp" });
vi.resetModules();
const listHttpRoute = (await import("./httproute-list")).default;
@@ -87,20 +74,4 @@ describe("utils/kubernetes/httproute-list", () => {
expect(logger.error).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();
});
});