Skip to content

Commit 72e1cc4

Browse files
authored
Merge pull request #364 from kthcloud/gpu-deployment-updates
General updates related to gpu deployment usage and the updated oauth2 package
2 parents 1d0f7a1 + e461a5e commit 72e1cc4

17 files changed

Lines changed: 488 additions & 219 deletions

bun.lockb

3.58 KB
Binary file not shown.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"@types/react-dom": "^18.3.7",
6868
"@typescript-eslint/eslint-plugin": "^7.18.0",
6969
"@typescript-eslint/parser": "^7.18.0",
70-
"@vitejs/plugin-react": "^4.7.0",
70+
"@vitejs/plugin-react": "^5.1.1",
7171
"@vitejs/plugin-react-swc": "^3.11.0",
7272
"babel-plugin-react-compiler": "^1.0.0",
7373
"eslint": "^9.39.4",

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Iconify from "./components/Iconify";
1616
import { ThemeModeContextProvider } from "./contexts/ThemeModeContext";
1717
import { AlertContextProvider } from "./contexts/AlertContext";
1818
import { AdminResourceContextProvider } from "./contexts/AdminResourceContext";
19+
import TokenExpiryModal from "./components/TokenExpiryModal";
1920

2021
export default function App() {
2122
return (
@@ -42,6 +43,7 @@ export default function App() {
4243
<ThemeProvider>
4344
<ScrollToTop />
4445
<BaseOptionChartStyle />
46+
<TokenExpiryModal />
4547
<Router />
4648
</ThemeProvider>
4749
</SnackbarProvider>

src/components/Callback.tsx

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,9 @@ import { useEffect } from "react";
22
import { useNavigate } from "react-router-dom";
33
import { useAuth } from "react-oidc-context";
44

5-
import {
6-
Box,
7-
Button,
8-
CircularProgress,
9-
Paper,
10-
Typography,
11-
} from "@mui/material";
5+
import { Box, Button, Paper, Typography } from "@mui/material";
126
import Page from "./Page";
7+
import LoadingPage from "./LoadingPage";
138

149
export default function Callback() {
1510
const navigate = useNavigate();
@@ -31,16 +26,7 @@ export default function Callback() {
3126
}, [auth.isLoading, auth.isAuthenticated, navigate]);
3227

3328
if (auth.isLoading) {
34-
return (
35-
<Page>
36-
<Box component="div" sx={{ minHeight: "100vh" }}>
37-
<CircularProgress />
38-
<Typography mt={2} variant="h6">
39-
Authenticating...
40-
</Typography>
41-
</Box>
42-
</Page>
43-
);
29+
return <LoadingPage />;
4430
}
4531

4632
if (auth.error) {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from "react";
2+
import { useAuth } from "react-oidc-context";
3+
import {
4+
Dialog,
5+
DialogTitle,
6+
DialogContent,
7+
DialogActions,
8+
Button,
9+
Typography,
10+
} from "@mui/material";
11+
12+
export default function TokenExpiryModal() {
13+
const auth = useAuth();
14+
const [open, setOpen] = React.useState(false);
15+
16+
React.useEffect(() => {
17+
return auth.events.addAccessTokenExpiring(() => {
18+
setOpen(true);
19+
});
20+
}, [auth.events]);
21+
22+
const handleContinue = async () => {
23+
try {
24+
await auth.signinSilent();
25+
setOpen(false);
26+
} catch (err) {
27+
// notistack insted
28+
console.error("Silent renew failed", err);
29+
}
30+
};
31+
32+
const handleLogout = () => {
33+
auth.removeUser();
34+
};
35+
36+
return (
37+
<Dialog open={open}>
38+
<DialogTitle>Session Expiring</DialogTitle>
39+
40+
<DialogContent>
41+
<Typography>
42+
Your session is about to expire due to inactivity. Would you like to
43+
stay signed in?
44+
</Typography>
45+
</DialogContent>
46+
47+
<DialogActions>
48+
<Button onClick={handleLogout} color="error">
49+
Log out
50+
</Button>
51+
52+
<Button onClick={handleContinue} variant="contained">
53+
Continue session
54+
</Button>
55+
</DialogActions>
56+
</Dialog>
57+
);
58+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Stack, TextField, IconButton, Chip, Typography } from "@mui/material";
2+
import AddIcon from "@mui/icons-material/Add";
3+
import { useState } from "react";
4+
5+
// Simple CEL validation function (stub, replace with real parser)
6+
const validateCel = (expr: string): boolean => {
7+
try {
8+
if (!expr) return true; // empty allowed
9+
// TODO: Replace with actual CEL parser/validation
10+
// For now: basic check for balanced parentheses
11+
let count = 0;
12+
for (const c of expr) {
13+
if (c === "(") count++;
14+
if (c === ")") count--;
15+
if (count < 0) return false;
16+
}
17+
return count === 0;
18+
} catch {
19+
return false;
20+
}
21+
};
22+
23+
interface CelExprBuilderProps {
24+
value: string[];
25+
onChange: (value: string[]) => void;
26+
label?: string;
27+
placeholder?: string;
28+
}
29+
30+
export default function CelExprBuilder({
31+
value,
32+
onChange,
33+
label = "CEL Expressions",
34+
placeholder = "Enter CEL expression",
35+
}: CelExprBuilderProps) {
36+
const [input, setInput] = useState("");
37+
38+
const addExpr = () => {
39+
const expr = input.trim();
40+
if (!expr) return;
41+
42+
const newValue = [...value, expr];
43+
onChange(newValue);
44+
setInput("");
45+
};
46+
47+
const removeExpr = (index: number) => {
48+
onChange(value.filter((_, i) => i !== index));
49+
};
50+
51+
return (
52+
<Stack spacing={1}>
53+
<Typography variant="subtitle2">{label}</Typography>
54+
55+
{/* Existing expressions as Chips */}
56+
<Stack direction="row" spacing={1} flexWrap="wrap">
57+
{value.map((expr, idx) => {
58+
const valid = validateCel(expr);
59+
return (
60+
<Chip
61+
key={idx}
62+
label={expr}
63+
color={valid ? "primary" : "error"}
64+
onDelete={() => removeExpr(idx)}
65+
/>
66+
);
67+
})}
68+
</Stack>
69+
70+
{/* Input field for new expression */}
71+
<Stack direction="row" spacing={1}>
72+
<TextField
73+
fullWidth
74+
size="small"
75+
placeholder={placeholder}
76+
value={input}
77+
onChange={(e) => setInput(e.target.value)}
78+
error={(input && !validateCel(input)) || false}
79+
helperText={input && !validateCel(input) ? "Invalid CEL syntax" : ""}
80+
/>
81+
<IconButton
82+
color="primary"
83+
onClick={addExpr}
84+
disabled={!input || !validateCel(input)}
85+
>
86+
<AddIcon />
87+
</IconButton>
88+
</Stack>
89+
</Stack>
90+
);
91+
}

src/components/admin/ClusterOverviewTab.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ export default function ClusterOverviewTab() {
3333
const hasDRA = zone.capabilities?.includes("dra");
3434

3535
return (
36-
<Card key={zone.name} variant="outlined">
36+
<Card
37+
key={zone.name}
38+
variant="outlined"
39+
sx={{
40+
opacity: zone.enabled === false ? 0.5 : 1, // gray out if disabled
41+
pointerEvents: zone.enabled === false ? "none" : "auto", // disable interactions
42+
}}
43+
>
3744
<CardContent>
3845
<Stack spacing={2}>
3946
{/* Header */}
@@ -42,10 +49,26 @@ export default function ClusterOverviewTab() {
4249
justifyContent="space-between"
4350
alignItems="center"
4451
>
45-
<Typography variant="subtitle1" fontWeight={600}>
52+
<Typography
53+
variant="subtitle1"
54+
fontWeight={600}
55+
color={
56+
zone.enabled === false
57+
? "text.disabled"
58+
: "text.primary"
59+
}
60+
>
4661
{zone.name}
4762
</Typography>
48-
<Typography variant="subtitle1" fontWeight={600}>
63+
<Typography
64+
variant="subtitle1"
65+
fontWeight={600}
66+
color={
67+
zone.enabled === false
68+
? "text.disabled"
69+
: "text.primary"
70+
}
71+
>
4972
{zone.description}
5073
</Typography>
5174
</Stack>
@@ -58,12 +81,14 @@ export default function ClusterOverviewTab() {
5881
label={cap.toUpperCase()}
5982
size="small"
6083
color={cap === "dra" ? "primary" : "default"}
84+
sx={{
85+
opacity: zone.enabled === false ? 0.5 : 1,
86+
}}
6187
/>
6288
))}
6389
</Stack>
6490

65-
{/* Optional DRA section */}
66-
{hasDRA && (
91+
{hasDRA && zone.enabled !== false && (
6792
<>
6893
<Divider />
6994
<DRAConfigPanel zone={zone} roles={roles} />

src/components/admin/DRAConfigPanel.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import useAdmin from "../../hooks/useAdmin";
1010
interface DRAConfigPanelProps {
1111
zone: {
1212
name: string;
13-
// extend later with draResources, policies, etc
1413
};
1514
roles: string[];
1615
}

src/components/admin/GPUClaimEditor.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,24 @@ export default function GpuClaimEditor({ value, onChange }: Props) {
103103
required
104104
/>
105105

106-
{/* TODO: CEL expr for selectors*/}
107-
{/* <Autocomplete
108-
multiple
109-
freeSolo
110-
options={["e"]}
111-
label="Selectors"
112-
value={gpu.selectors}
113-
onChange={(_, sel) => {
114-
updateRequested(idx, {
115-
selectors: sel,
116-
});
117-
}}
118-
/> */}
106+
{/*gpu.selectors?.find(((s as any)) => "cel" in s) && (
107+
<CelExprBuilder
108+
value={[
109+
(gpu.selectors!.find(((s as any)) => "cel" in s) as any).cel
110+
.expression,
111+
]} // wrap as array
112+
onChange={(newExpressions) => {
113+
const newSelectors = gpu.selectors!.map((s) =>
114+
"cel" in s
115+
? { cel: { expression: newExpressions[0] } } // update CEL
116+
: s
117+
);
118+
updateRequested(idx, { selectors: newSelectors });
119+
}}
120+
label="Selectors (CEL expression)"
121+
placeholder="gpu.memory > 4 && gpu.computeCapability >= 7.0"
122+
/>
123+
)*/}
119124

120125
<Stack spacing={2}>
121126
<Typography>{"Driver configuration"}</Typography>

src/components/render/Resource.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,18 @@ export const renderStatusCode = (row: Resource) => {
244244
);
245245
};
246246

247+
export const renderDeploymentGPU = (gpus: Deployment["specs"]["gpus"]) => {
248+
if (!gpus || gpus.length <= 0) return null;
249+
return (
250+
<Label
251+
variant="ghost"
252+
startIcon={<Iconify icon="mdi:gpu" sx={{ opacity: 0.65 }} />}
253+
>
254+
{gpus.length > 1 ? gpus.length : gpus[0].claimName + "/" + gpus[0].name}
255+
</Label>
256+
);
257+
};
258+
247259
export const renderZone = (row: Resource, zones: ZoneRead[]) => {
248260
if (!row.zone || !zones) {
249261
return <></>;

0 commit comments

Comments
 (0)