Skip to content

Commit 796abc9

Browse files
committed
feat/trpc: Add support for trpc and improve the api response time
Signed-off-by: Kuldeep <de6p97@gmail.com>
1 parent a4742ce commit 796abc9

34 files changed

+3547
-6337
lines changed

backend/.babelrc

Lines changed: 0 additions & 31 deletions
This file was deleted.

backend/README.md

Lines changed: 70 additions & 1586 deletions
Large diffs are not rendered by default.

backend/docs/kubernetes_api_client.md

Lines changed: 1612 additions & 0 deletions
Large diffs are not rendered by default.

backend/package.json

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,36 @@
11
{
2-
"name": "kubernetes",
3-
"version": "v1.30.0",
4-
"description": "JS API client generated by OpenAPI Generator",
5-
"license": "Unlicense",
6-
"main": "dist/index.js",
2+
"name": "@volcano/backend",
3+
"version": "1.30.0",
4+
"description": "Official API for Volcano",
5+
"license": "MIT",
6+
"main": "index.js",
77
"scripts": {
8-
"dev": "nodemon src/server.js",
9-
"build": "babel src -d dist",
10-
"prepare": "npm run build",
11-
"test": "vitest",
12-
"test:ui": "vitest --ui"
8+
"dev": "npx ts-node src/app.ts"
139
},
1410
"browser": {
1511
"fs": false
1612
},
17-
"type": "module",
1813
"dependencies": {
19-
"@babel/cli": "^7.0.0",
2014
"@kubernetes/client-node": "^1.0.0",
15+
"@trpc/server": "^10.45.2",
2116
"cors": "^2.8.5",
2217
"dotenv": "^16.4.5",
2318
"express": "^4.21.0",
2419
"lodash": "^4.17.21",
25-
"superagent": "^5.3.0"
20+
"superagent": "^5.3.0",
21+
"superjson": "^2.2.2",
22+
"zod": "^3.24.2"
2623
},
2724
"devDependencies": {
28-
"@babel/core": "^7.0.0",
29-
"@babel/plugin-proposal-decorators": "^7.0.0",
30-
"@babel/plugin-proposal-do-expressions": "^7.0.0",
31-
"@babel/plugin-proposal-export-default-from": "^7.0.0",
32-
"@babel/plugin-proposal-function-bind": "^7.0.0",
33-
"@babel/plugin-proposal-function-sent": "^7.0.0",
34-
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
35-
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
36-
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
37-
"@babel/plugin-syntax-import-meta": "^7.0.0",
38-
"@babel/plugin-transform-class-properties": "^7.25.9",
39-
"@babel/plugin-transform-export-namespace-from": "^7.25.9",
40-
"@babel/plugin-transform-json-strings": "^7.25.9",
41-
"@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
42-
"@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6",
43-
"@babel/plugin-transform-numeric-separator": "^7.25.9",
44-
"@babel/plugin-transform-optional-chaining": "^7.25.9",
45-
"@babel/preset-env": "^7.0.0",
46-
"@babel/register": "^7.0.0",
25+
"@types/cors": "^2.8.17",
26+
"@types/express": "^4.17.17",
27+
"@types/node": "^22.13.10",
4728
"expect.js": "^0.3.1",
4829
"mocha": "^11.1.0",
4930
"nodemon": "^3.1.9",
5031
"sinon": "^7.2.0",
51-
"supertest": "^7.0.0"
32+
"ts-node": "^10.9.2",
33+
"typescript": "^5.8.2"
5234
},
5335
"files": [
5436
"dist"

backend/src/api/context.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const createTrpcContext = async (opts: any) => {
2+
return {
3+
session: null,
4+
};
5+
};
6+
7+
export type TrpcContext = Awaited<ReturnType<typeof createTrpcContext>>;

backend/src/api/router.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { jobsRouter } from "./router/jobs/jobs";
2+
import { namespaceRouter } from "./router/namespaces/namespaces";
3+
import { podRouter } from "./router/pods/pods";
4+
import { queueRouter } from "./router/queues/queues";
5+
import { router } from "./trpc";
6+
7+
export const appRouter = router({
8+
jobsRouter,
9+
podRouter,
10+
namespaceRouter,
11+
queueRouter,
12+
});
13+
14+
export type AppRouter = typeof appRouter;

backend/src/api/router/helpers.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { k8sApi, k8sCoreApi } from "../../utils/k8s";
2+
3+
export const fetchJobs = async (
4+
namespace: string,
5+
search: string,
6+
queue: string,
7+
status: string,
8+
page: number,
9+
pageSize: number,
10+
) => {
11+
let response;
12+
if (namespace === "" || namespace === "All") {
13+
response = await k8sApi.listClusterCustomObject({
14+
group: "batch.volcano.sh",
15+
version: "v1alpha1",
16+
plural: "jobs",
17+
pretty: "true",
18+
});
19+
} else {
20+
response = await k8sApi.listNamespacedCustomObject({
21+
group: "batch.volcano.sh",
22+
version: "v1alpha1",
23+
namespace,
24+
plural: "jobs",
25+
pretty: "true",
26+
});
27+
}
28+
29+
let filteredJobs = response.items || [];
30+
31+
if (search) {
32+
filteredJobs = filteredJobs.filter((job: any) =>
33+
job.metadata.name.toLowerCase().includes(search.toLowerCase()),
34+
);
35+
}
36+
37+
if (queue && queue !== "All") {
38+
filteredJobs = filteredJobs.filter(
39+
(job: any) => job.spec.queue === queue,
40+
);
41+
}
42+
43+
if (status && status !== "All") {
44+
filteredJobs = filteredJobs.filter(
45+
(job: any) => job.status.state.phase === status,
46+
);
47+
}
48+
49+
const startIndex = (page - 1) * pageSize;
50+
const endIndex = startIndex + pageSize;
51+
const paginatedJobs = filteredJobs.slice(startIndex, endIndex);
52+
53+
return {
54+
items: paginatedJobs,
55+
totalCount: filteredJobs.length,
56+
};
57+
};
58+
59+
interface Job {
60+
status?:
61+
| {
62+
state?: {
63+
phase?: string;
64+
};
65+
}
66+
| string;
67+
spec?: {
68+
minAvailable?: number;
69+
queue?: string;
70+
};
71+
metadata: {
72+
name: string;
73+
};
74+
}
75+
76+
export function getJobState(job: Job) {
77+
if (typeof job.status === "object" && job.status?.state) {
78+
return job.status.state.phase || "Unknown";
79+
}
80+
if (typeof job.status === "string") {
81+
return job.status;
82+
}
83+
return "Unknown";
84+
}
85+
86+
export const fetchQueues = async (
87+
search: string,
88+
state: string,
89+
page: number,
90+
pageSize: number,
91+
) => {
92+
const response = await k8sApi.listClusterCustomObject({
93+
group: "scheduling.volcano.sh",
94+
version: "v1beta1",
95+
plural: "queues",
96+
});
97+
98+
let filteredQueues = response.items || [];
99+
100+
if (search) {
101+
filteredQueues = filteredQueues.filter((queue: any) =>
102+
queue.metadata.name.toLowerCase().includes(search.toLowerCase()),
103+
);
104+
}
105+
106+
if (state && state !== "All") {
107+
filteredQueues = filteredQueues.filter(
108+
(pod: any) => pod.status.state === state,
109+
);
110+
}
111+
112+
const startIndex = (page - 1) * pageSize;
113+
const endIndex = startIndex + pageSize;
114+
const paginatedQueues = filteredQueues.slice(startIndex, endIndex);
115+
116+
return {
117+
items: paginatedQueues,
118+
totalCount: filteredQueues.length,
119+
};
120+
};
121+
122+
export const fetchPods = async (
123+
namespace: string,
124+
search: string,
125+
status: string,
126+
page: number,
127+
pageSize: number,
128+
) => {
129+
let response;
130+
if (namespace === "" || namespace === "All") {
131+
response = await k8sCoreApi.listPodForAllNamespaces();
132+
} else {
133+
response = await k8sCoreApi.listNamespacedPod({
134+
namespace,
135+
});
136+
}
137+
138+
let filteredPods = response.items || [];
139+
140+
if (search) {
141+
filteredPods = filteredPods.filter((pod: any) =>
142+
pod.metadata.name.toLowerCase().includes(search.toLowerCase()),
143+
);
144+
}
145+
146+
if (status && status !== "All") {
147+
filteredPods = filteredPods.filter(
148+
(pod: any) => pod.status.phase === status,
149+
);
150+
}
151+
152+
const startIndex = (page - 1) * pageSize;
153+
const endIndex = startIndex + pageSize;
154+
const paginatedPods = filteredPods.slice(startIndex, endIndex);
155+
156+
return {
157+
items: paginatedPods,
158+
totalCount: filteredPods.length,
159+
};
160+
};

backend/src/api/router/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import yaml from "js-yaml";
2+
import { k8sApi } from "../../../utils/k8s";
3+
import { procedure, router } from "../../trpc";
4+
import { fetchJobs, getJobState } from "../helpers";
5+
import { getJobInputSchema, getJobsInputSchema } from "./schema";
6+
7+
export const jobsRouter = router({
8+
getJobs: procedure.input(getJobsInputSchema).query(async ({ input }) => {
9+
const {
10+
namespace = "",
11+
search = "",
12+
queue = "",
13+
status = "",
14+
page = 1,
15+
pageSize = 10,
16+
} = input;
17+
console.log("Fetching jobs with params:", {
18+
namespace,
19+
search,
20+
queue,
21+
status,
22+
page,
23+
pageSize,
24+
});
25+
26+
const filteredJobs = await fetchJobs(
27+
namespace,
28+
search,
29+
queue,
30+
status,
31+
page,
32+
pageSize,
33+
);
34+
35+
return filteredJobs;
36+
}),
37+
getJob: procedure.input(getJobInputSchema).query(async ({ input }) => {
38+
const { namespace, name } = input;
39+
const response = await k8sApi.getNamespacedCustomObject({
40+
group: "batch.volcano.sh",
41+
version: "v1alpha1",
42+
namespace,
43+
plural: "jobs",
44+
name,
45+
});
46+
return response;
47+
}),
48+
getJobYaml: procedure.input(getJobInputSchema).query(async ({ input }) => {
49+
const { namespace, name } = input;
50+
const response = await k8sApi.getNamespacedCustomObject({
51+
group: "batch.volcano.sh",
52+
version: "v1alpha1",
53+
namespace,
54+
plural: "jobs",
55+
name,
56+
});
57+
58+
const formattedYaml = yaml.dump(response, {
59+
indent: 2,
60+
lineWidth: -1,
61+
noRefs: true,
62+
sortKeys: false,
63+
});
64+
65+
return formattedYaml;
66+
}),
67+
getAllJobs: procedure.query(async () => {
68+
const response = await k8sApi.listClusterCustomObject({
69+
group: "batch.volcano.sh",
70+
version: "v1alpha1",
71+
plural: "jobs",
72+
pretty: "true",
73+
});
74+
75+
const jobs = response.items.map((job: any) => ({
76+
...job,
77+
status: {
78+
state: job.status?.state || getJobState(job),
79+
phase:
80+
job.status?.phase || job.spec?.minAvailable
81+
? "Running"
82+
: "Unknown",
83+
},
84+
}));
85+
86+
return {
87+
items: jobs,
88+
totalCount: jobs.length,
89+
};
90+
}),
91+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { z } from "zod";
2+
3+
export const getJobsInputSchema = z.object({
4+
namespace: z.string().optional(),
5+
search: z.string().optional(),
6+
queue: z.string().optional(),
7+
status: z.string().optional(),
8+
page: z.number().optional(),
9+
pageSize: z.number().optional(),
10+
});
11+
12+
export const getJobInputSchema = z.object({
13+
namespace: z.string(),
14+
name: z.string(),
15+
});

0 commit comments

Comments
 (0)