Skip to content

Commit 00fe34b

Browse files
authored
Merge pull request #337 from kthcloud/main
Update release from latest main
2 parents c984e91 + f44b66a commit 00fe34b

File tree

14 files changed

+246
-71
lines changed

14 files changed

+246
-71
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ kthcloud maintained:
2222

2323
- [go-deploy](https://github.com/kthcloud/go-deploy): Backend for creation, and management of resources
2424
- [alert](https://alert.app.cloud.cbh.kth.se/): Provides alerts
25-
- [llama](https://llama.app.cloud.cbh.kth.se/): Enables genAI features
26-
- [llama-prefetch](https://github.com/kthcloud/llama-prefetch/): Reduces genAI features latency by prefetching queries
2725
- [kthcloud iam](https://iam.cloud.cbh.kth.se): Provides user auth
2826

2927
External:

src/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// keycloak
2-
import { ReactKeycloakProvider } from "@react-keycloak/web";
2+
import { AuthContextWrapperProvider } from "./contexts/AuthContextWrapper";
33
import { keycloak } from "./keycloak";
44
// routes
55
import Router from "./Router";
@@ -17,7 +17,7 @@ import { AlertContextProvider } from "./contexts/AlertContext";
1717

1818
export default function App() {
1919
return (
20-
<ReactKeycloakProvider authClient={keycloak}>
20+
<AuthContextWrapperProvider authClient={keycloak}>
2121
<AlertContextProvider>
2222
<ResourceContextProvider>
2323
<ThemeModeContextProvider>
@@ -45,6 +45,6 @@ export default function App() {
4545
</ThemeModeContextProvider>
4646
</ResourceContextProvider>
4747
</AlertContextProvider>
48-
</ReactKeycloakProvider>
48+
</AuthContextWrapperProvider>
4949
);
5050
}

src/api/deploy/gpuLeases.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { GpuLeaseCreate } from "@kthcloud/go-deploy-types/types/v2/body";
22
import { Jwt, Uuid } from "../../types";
33

4-
export const listGpuLeases = async (token: Jwt, vmId?: Uuid) => {
4+
export const listGpuLeases = async (token: Jwt, vmId?: Uuid, all?: boolean) => {
55
const vmIdQuery = vmId ? `?vmId=${encodeURIComponent(vmId)}` : "";
6-
const url = `${import.meta.env.VITE_DEPLOY_API_URL}/gpuLeases${vmIdQuery}`;
6+
const allQuery =
7+
all !== undefined
8+
? (vmIdQuery ? "&" : "?") + "all=" + (all !== false ? "true" : "false")
9+
: "";
10+
const url = `${import.meta.env.VITE_DEPLOY_API_URL}/gpuLeases${vmIdQuery + allQuery}`;
711
const response = await fetch(url, {
812
method: "GET",
913
headers: {

src/components/ProtectedRoute.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { Navigate } from "react-router-dom";
22
import { useKeycloak } from "@react-keycloak/web";
33
import LoadingPage from "./LoadingPage";
4+
import { useContext } from "react";
5+
import { AuthContextWrapper } from "../contexts/AuthContextWrapper";
46

57
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
8+
const { error } = useContext(AuthContextWrapper);
69
const { keycloak, initialized } = useKeycloak();
710

811
const renderPage = (children: React.ReactNode) => {
9-
if (!initialized) {
12+
if (!initialized && !error) {
1013
return <LoadingPage />;
1114
} else if (initialized && keycloak.authenticated) {
1215
return children;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// keycloak
2+
import { ReactKeycloakProvider } from "@react-keycloak/web";
3+
import Keycloak from "keycloak-js";
4+
5+
import {
6+
AuthClientEvent,
7+
AuthClientError,
8+
} from "@react-keycloak/core/lib/types";
9+
import { createContext, useState } from "react";
10+
11+
type AuthContextWrapperType = {
12+
events: AuthClientEvent[];
13+
error?: AuthClientError;
14+
};
15+
16+
const initialState: AuthContextWrapperType = {
17+
events: [],
18+
};
19+
20+
export const AuthContextWrapper = createContext(initialState);
21+
22+
export const AuthContextWrapperProvider = ({
23+
authClient,
24+
children,
25+
}: {
26+
authClient: Keycloak;
27+
children: React.ReactNode;
28+
}) => {
29+
const [events, setEvents] = useState<AuthClientEvent[]>([]);
30+
const [error, setError] = useState<AuthClientError | undefined>(undefined);
31+
32+
const handleEvent = (
33+
event: AuthClientEvent,
34+
error: AuthClientError | undefined
35+
) => {
36+
setEvents([...events, event]);
37+
if (error) {
38+
setError(error);
39+
}
40+
};
41+
return (
42+
<AuthContextWrapper.Provider value={{ events, error }}>
43+
<ReactKeycloakProvider authClient={authClient} onEvent={handleEvent}>
44+
{children}
45+
</ReactKeycloakProvider>
46+
</AuthContextWrapper.Provider>
47+
);
48+
};

src/layouts/dashboard/Menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export default function Menu() {
153153
{t("menu-status")}
154154
</MenuItem>
155155
<MenuItem
156-
href={"https://maia.cloud.cbh.kth.se/"}
156+
href={"https://maia.app.cloud.cbh.kth.se/maia"}
157157
component={Link}
158158
onClick={handleClose}
159159
target="_blank"

src/locales/en.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
"line-wrap": "Line wrap",
208208
"visibility-saving": "Saving visibility...",
209209
"could-not-save-visibility": "Could not save visibility: ",
210-
"visibility-subheader": "Choose whether to make this deployment hidden from the internet",
210+
"visibility-subheader": "Choose the visibility level of this deployment",
211211
"lease-gpu": "Lease GPU",
212212
"lease-gpu-forever": "Lease GPU forever",
213213
"renew-gpu-lease": "Renew GPU lease",
@@ -474,8 +474,7 @@
474474
"kth-digitalization-platform": "KTH Digitalisation Platform",
475475
"computational-support": "We receive invaluable computational support from ",
476476
"funding": "Funding",
477-
"disclaimer": "Disclaimer",
478-
"genai-long": "AI generated content may be inaccurate or misleading, always verify the information. All AI generated content on this page is marked with the following symbol",
477+
479478
"name-cannot-be-empty": "Name cannot be empty",
480479
"saving-name": "Saving name...",
481480
"rfc-1035-1": " must adhere to ",
@@ -534,6 +533,11 @@
534533
"too-many-run-args": "Too many run args, max 100",
535534
"pending-migrations": "Pending migrations",
536535
"migration": "Migration",
537-
"of": "of"
536+
"of": "of",
537+
"error-connecting-to-iam": "Error connecting to IAM",
538+
"admin-visibility-auth": "Private (auth proxy)",
539+
"visibility-public-tooltip": "Your deployment is publicly accessible on the internet.",
540+
"visibility-private-tooltip": "Your deployment is private and not reachable on the internet. Only internal traffic is allowed in (traffic between deployments).",
541+
"visibility-auth-tooltip": "Your deployment is private and reachable on the internet through an authentication proxy, this limits the allowed traffic."
538542
}
539543
}

src/locales/se.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
"menu-manage-resources": "Hantera resurser",
152152
"error-could-not-fetch-gpus": "Kunde inte hämta grafikkort",
153153
"could-not-fetch-profile": "Profilen kunde inte hämtas: ",
154-
"visibility-subheader": "Välj om din app ska synas på webben",
154+
"visibility-subheader": "Välj synlighetsnivån av din deployment",
155155
"secrets": "hemligheter",
156156
"onboarding-resources": "Resurser på kthcloud",
157157
"deleting": "Raderar...",
@@ -471,8 +471,7 @@
471471
"computational-support": "Vi får även stöd i form av hårdvara från ",
472472
"funding": "Finansiellt stöd",
473473
"menu-maia": "MAIA",
474-
"disclaimer": "Ansvarsfriskrivning",
475-
"genai-long": "Innehåll genererat av AI kan vara felaktigt eller missvisande, verifiera alltid informationen. Allt innehåll genererat av AI på denna sida är märkt med följande symbol",
474+
476475
"name-cannot-be-empty": "Namnfältet är obligatoriskt",
477476
"saving-name": "Sparar namn...",
478477
"rfc-1035-1": " måste följa ",
@@ -533,6 +532,11 @@
533532
"too-many-run-args": "För många argument, max 100",
534533
"pending-migrations": "Pågående överföringar",
535534
"migration": "Överföring",
536-
"of": "av"
535+
"of": "av",
536+
"error-connecting-to-iam": "Fel vid anslutning till IAM",
537+
"admin-visibility-auth": "Privat (auth proxy)",
538+
"visibility-public-tooltip": "Din deployment är publikt åtkomlig på internet.",
539+
"visibility-private-tooltip": "Din deployment är privat och inte åtkomlig på internet. Enbart intern trafik mellan appar är tillåten.",
540+
"visibility-auth-tooltip": "Din deployment är privat och åtkomlig på internet via en autentiserings-proxy, detta begränsar tillåten trafik."
537541
}
538542
}

src/pages/admin/Admin.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ export const Admin = () => {
118118
},
119119
async () => {
120120
try {
121-
const response = await listGpuLeases(keycloak.token!);
121+
const response = await listGpuLeases(
122+
keycloak.token!,
123+
undefined,
124+
true
125+
);
122126
setDbGPUs(response);
123127
} catch (error: any) {
124128
errorHandler(error).forEach((e) =>

src/pages/edit/Specs.tsx

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const Specs = ({ resource }: { resource: Resource }) => {
5555
const [maxRam, setMaxRam] = useState<number>(
5656
resource.type === "vm" ? MAX_RAM_VM : MAX_RAM_DEPLOYMENT
5757
);
58-
const [maxReplicas, setMaxReplicas] = useState<number>(MAX_REPLICAS);
58+
const [maxReplicas, _] = useState<number>(MAX_REPLICAS);
5959

6060
const getInitialCpu = () => {
6161
return resource.specs.cpuCores || 0;
@@ -72,6 +72,89 @@ export const Specs = ({ resource }: { resource: Resource }) => {
7272
const [loading, setLoading] = useState<boolean>(false);
7373
const [editing, setEditing] = useState<boolean>(false);
7474

75+
const toClosestStep = (value: number, step: number) => {
76+
return Math.round(Math.floor(value / step) * step * 100) / 100;
77+
};
78+
79+
const calculateResourcesLeft = (
80+
quota: { cpuCores: number; ram: number },
81+
usage: { cpuCores: number; ram: number },
82+
currentUsage: {
83+
cpuCores: number;
84+
ram: number;
85+
replicas?: number;
86+
}
87+
): {
88+
totalCoresLeft: number;
89+
totalRamLeft: number;
90+
} => {
91+
const usageWithoutDepl = {
92+
cpuCores: Math.max(
93+
usage.cpuCores - currentUsage.cpuCores * (currentUsage.replicas ?? 1),
94+
0
95+
),
96+
ram: Math.max(
97+
usage.ram - currentUsage.ram * (currentUsage.replicas ?? 1),
98+
0
99+
),
100+
};
101+
102+
const totalCoresLeft = quota.cpuCores - usageWithoutDepl.cpuCores;
103+
const totalRamLeft = quota.ram - usageWithoutDepl.ram;
104+
105+
return { totalCoresLeft, totalRamLeft };
106+
};
107+
108+
useEffect(() => {
109+
if (user) {
110+
const currentUsage = {
111+
cpuCores: resource.specs.cpuCores ?? 0,
112+
ram: resource.specs.ram ?? 0,
113+
replicas: (resource as Deployment).specs.replicas,
114+
};
115+
116+
const { totalCoresLeft, totalRamLeft } = calculateResourcesLeft(
117+
user.quota,
118+
user.usage,
119+
currentUsage
120+
);
121+
122+
const newmax = {
123+
cpuCores: toClosestStep(
124+
Math.ceil(
125+
(replicas !== 0 ? totalCoresLeft / replicas : totalCoresLeft) * 10
126+
) / 10,
127+
resource.type === "vm" ? STEP_VM : STEP_DEPLOYMENT
128+
),
129+
ram: toClosestStep(
130+
Math.ceil(
131+
(replicas !== 0 ? totalRamLeft / replicas : totalRamLeft) * 10
132+
) / 10,
133+
resource.type === "vm" ? STEP_VM : STEP_DEPLOYMENT
134+
),
135+
};
136+
137+
setMaxCpu(
138+
// Set the maxCpu to the highest of the new max or the current value.
139+
// This since the current value has been allowed previously, but the user is actually exceeding their quota.
140+
Math.max(
141+
newmax.cpuCores,
142+
replicas !== 0
143+
? currentUsage.cpuCores / replicas
144+
: currentUsage.cpuCores
145+
)
146+
);
147+
setMaxRam(
148+
// Set the maxRam to the highest of the new max or the current value.
149+
// This since the current value has been allowed previously, but the user is actually exceeding their quota.
150+
Math.max(
151+
newmax.ram,
152+
replicas !== 0 ? currentUsage.ram / replicas : currentUsage.ram
153+
)
154+
);
155+
}
156+
}, [user, resource, cpu, ram, replicas]);
157+
75158
const updateSpecs = () => {
76159
if (resource.type === "deployment") {
77160
const d = resource as Deployment;
@@ -123,15 +206,6 @@ export const Specs = ({ resource }: { resource: Resource }) => {
123206
[cpu, ram, replicas, maxCpu, maxRam, maxReplicas]
124207
);
125208

126-
// Set max values
127-
useEffect(() => {
128-
setMaxCpu(20);
129-
setMaxRam(20);
130-
setMaxReplicas(20);
131-
132-
// eslint-disable-next-line react-hooks/exhaustive-deps
133-
}, [resource]);
134-
135209
const isSame = () => {
136210
if (resource.type === "deployment") {
137211
const d = resource as Deployment;
@@ -342,7 +416,7 @@ export const Specs = ({ resource }: { resource: Resource }) => {
342416
<Grid xs={12}>
343417
<Slider
344418
value={cpu}
345-
onChange={(_, v) => setCpu(v as number)}
419+
onChange={(_: any, v: any) => setCpu(v as number)}
346420
min={resource.type === "vm" ? MIN_CPU_VM : MIN_CPU_DEPLOYMENT}
347421
max={maxCpu}
348422
step={resource.type === "vm" ? STEP_VM : STEP_DEPLOYMENT}
@@ -389,7 +463,7 @@ export const Specs = ({ resource }: { resource: Resource }) => {
389463
<Grid xs={12}>
390464
<Slider
391465
value={ram}
392-
onChange={(_, v) => setRam(v as number)}
466+
onChange={(_: any, v: any) => setRam(v as number)}
393467
min={resource.type === "vm" ? MIN_RAM_VM : MIN_RAM_DEPLOYMENT}
394468
max={maxRam}
395469
step={resource.type === "vm" ? STEP_VM : STEP_DEPLOYMENT}
@@ -464,7 +538,7 @@ export const Specs = ({ resource }: { resource: Resource }) => {
464538
<Grid xs={12}>
465539
<Slider
466540
value={replicas}
467-
onChange={(_, v) => setReplicas(v as number)}
541+
onChange={(_: any, v: any) => setReplicas(v as number)}
468542
min={MIN_REPLICAS}
469543
max={maxReplicas}
470544
step={1}

0 commit comments

Comments
 (0)