From c3f8c6dae85a0ccea23e89d712ef6f22d9ea8364 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 23 Dec 2024 10:07:13 +0100 Subject: [PATCH 01/12] feat: configure disk storage for Renku 2.0 sessions Closes #3451. --- client/src/features/sessionsV2/sessionsV2.types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/features/sessionsV2/sessionsV2.types.ts b/client/src/features/sessionsV2/sessionsV2.types.ts index 087dddbefc..02f66c82e6 100644 --- a/client/src/features/sessionsV2/sessionsV2.types.ts +++ b/client/src/features/sessionsV2/sessionsV2.types.ts @@ -45,6 +45,7 @@ export type SessionLauncher = { creation_date: string; description?: string; resource_class_id?: number; + disk_storage?: number; environment: SessionLauncherEnvironment; }; @@ -98,6 +99,7 @@ export type AddSessionLauncherParams = { name: string; project_id: string; resource_class_id?: number; + disk_storage?: number; environment: SessionLauncherEnvironmentParams; }; @@ -106,6 +108,7 @@ export interface UpdateSessionLauncherParams { description?: string; name?: string; resource_class_id?: number; + disk_storage?: number | null; environment?: SessionLauncherEnvironmentParams; } From ca842e45065ea8da332d7fb407c29fdcd17e8ebe Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 23 Dec 2024 11:29:09 +0100 Subject: [PATCH 02/12] wip --- .../sessionsV2/SessionView/SessionView.tsx | 63 ++++++++++-------- .../SessionModals/ModifyResourcesLauncher.tsx | 66 ++++++++++++++++++- .../src/features/sessionsV2/sessionsV2.api.ts | 2 +- .../features/sessionsV2/sessionsV2.types.ts | 2 +- 4 files changed, 101 insertions(+), 32 deletions(-) diff --git a/client/src/features/sessionsV2/SessionView/SessionView.tsx b/client/src/features/sessionsV2/SessionView/SessionView.tsx index 67406a475c..882a4dd0fc 100644 --- a/client/src/features/sessionsV2/SessionView/SessionView.tsx +++ b/client/src/features/sessionsV2/SessionView/SessionView.tsx @@ -266,7 +266,9 @@ export function SessionView({ name: launcherResourceClass.name, cpu: launcherResourceClass.cpu, memory: `${launcherResourceClass.memory}G`, - storage: `${launcherResourceClass.default_storage}G`, + storage: `${ + launcher?.disk_storage ?? launcherResourceClass.default_storage + }G`, gpu: launcherResourceClass.gpu, }} /> @@ -374,27 +376,29 @@ export function SessionView({

Default Resource Class

- - - - Set resource class - - - } - requestedPermission="write" - userPermissions={permissions} - /> + {launcher && ( + + + + Set resource class + + + } + requestedPermission="write" + userPermissions={permissions} + /> + )}
{resourceDetails} {launcherResourceClass && !userLauncherResourceClass && ( @@ -403,12 +407,15 @@ export function SessionView({ You do not have access to this resource class.

)} - + {launcher && ( + + )}
diff --git a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx index 9938cc98b4..1e493499ab 100644 --- a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx @@ -2,7 +2,15 @@ import cx from "classnames"; import { useCallback, useEffect, useState } from "react"; import { CheckLg, XLg } from "react-bootstrap-icons"; import { SingleValue } from "react-select"; -import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { + Button, + Input, + Label, + Modal, + ModalBody, + ModalFooter, + ModalHeader, +} from "reactstrap"; import { SuccessAlert } from "../../../../components/Alert"; import { Loader } from "../../../../components/Loader"; import { useGetResourcePoolsQuery } from "../../../dataServices/computeResources.api"; @@ -18,7 +26,8 @@ interface ModifyResourcesLauncherModalProps { isOpen: boolean; toggleModal: () => void; resourceClassId?: number; - sessionLauncherId?: string; + diskStorage?: number; + sessionLauncherId: string; } export function ModifyResourcesLauncherModal({ @@ -26,6 +35,7 @@ export function ModifyResourcesLauncherModal({ sessionLauncherId, toggleModal, resourceClassId, + diskStorage, }: ModifyResourcesLauncherModalProps) { const [updateSessionLauncher, result] = useUpdateSessionLauncherMutation(); const { @@ -37,12 +47,30 @@ export function ModifyResourcesLauncherModal({ const [currentSessionClass, setCurrentSessionClass] = useState< ResourceClass | undefined >(undefined); + const [currentDiskStorage, setCurrentDiskStorage] = useState< + number | undefined + >(undefined); const onChange = useCallback((newValue: SingleValue) => { if (newValue) { setCurrentSessionClass(newValue); } }, []); + const onChangeDiskStorage = useCallback((newValue: number | null) => { + if (newValue) { + setCurrentDiskStorage(newValue); + } else { + setCurrentDiskStorage(undefined); + } + }, []); + const toggleDiskStorage = useCallback(() => { + setCurrentDiskStorage((oldValue) => { + if (oldValue == null && currentSessionClass) { + return currentSessionClass.default_storage; + } + return undefined; + }); + }, [currentSessionClass]); const onModifyResources = useCallback(() => { if (currentSessionClass) { @@ -60,6 +88,20 @@ export function ModifyResourcesLauncherModal({ setCurrentSessionClass(currentSessionClass); }, [resourceClassId, resourcePools]); + useEffect(() => { + setCurrentDiskStorage(diskStorage); + }, [diskStorage]); + + useEffect(() => { + if (!isOpen) { + const currentSessionClass = resourcePools + ?.flatMap((pool) => pool.classes) + .find((c) => c.id === resourceClassId); + setCurrentSessionClass(currentSessionClass); + setCurrentDiskStorage(diskStorage); + } + }, [diskStorage, isOpen, resourceClassId, resourcePools]); + const selector = isLoadingResources ? ( ) : !resourcePools || resourcePools.length == 0 || isErrorResources ? ( @@ -102,6 +144,26 @@ export function ModifyResourcesLauncherModal({ session’ in the session options.

{selector}
+
+ Disk Storage:{" "} + + {currentDiskStorage ? ( + <>{currentDiskStorage}GB + ) : ( + <>{currentSessionClass?.default_storage}GB (default) + )} + +
+ + +
+
diff --git a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx index 6a92d7cc7e..ef17143114 100644 --- a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx @@ -22,7 +22,19 @@ import { useCallback, useState } from "react"; import { XLg } from "react-bootstrap-icons"; import { Link } from "react-router-dom-v5-compat"; import { SingleValue } from "react-select"; -import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; +import { + Button, + FormText, + Input, + InputGroup, + InputGroupText, + Label, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + UncontrolledTooltip, +} from "reactstrap"; import { useGetResourceClassByIdQuery, useGetResourcePoolsQuery, @@ -34,10 +46,14 @@ import { ErrorOrNotAvailableResourcePools, FetchingResourcePools, } from "./ResourceClassWarning"; +import { + MIN_SESSION_STORAGE_GB, + STEP_SESSION_STORAGE_GB, +} from "../../../session/startSessionOptions.constants"; interface SelectResourceClassModalProps { isOpen: boolean; - onContinue: (env: ResourceClass) => void; + onContinue: (env: ResourceClass, diskStorage: number | undefined) => void; projectUrl: string; resourceClassId?: number; isCustom: boolean; @@ -58,6 +74,9 @@ export function SelectResourceClassModal({ const [currentSessionClass, setCurrentSessionClass] = useState< ResourceClass | undefined >(undefined); + const [currentDiskStorage, setCurrentDiskStorage] = useState< + number | undefined + >(undefined); const { data: launcherClass, isLoading: isLoadingLauncherClass } = useGetResourceClassByIdQuery(resourceClassId ?? skipToken); @@ -67,12 +86,27 @@ export function SelectResourceClassModal({ setCurrentSessionClass(newValue); } }, []); + const onChangeDiskStorage = useCallback((newValue: number | null) => { + if (newValue) { + setCurrentDiskStorage(newValue); + } else { + setCurrentDiskStorage(undefined); + } + }, []); + const toggleDiskStorage = useCallback(() => { + setCurrentDiskStorage((oldValue) => { + if (oldValue == null && currentSessionClass) { + return currentSessionClass.default_storage; + } + return undefined; + }); + }, [currentSessionClass]); const onClick = useCallback(() => { if (currentSessionClass) { - onContinue(currentSessionClass); + onContinue(currentSessionClass, currentDiskStorage); } - }, [currentSessionClass, onContinue]); + }, [currentDiskStorage, currentSessionClass, onContinue]); const selector = isLoading ? ( @@ -116,7 +150,7 @@ export function SelectResourceClassModal({

You do not have access to the default resource class of this session launcher. Please select one of your available resource classes to - continue.” + continue.

)} {launcherClass && ( @@ -128,6 +162,56 @@ export function SelectResourceClassModal({

)}
{selector}
+ {currentSessionClass && ( +
+
+ Disk Storage:{" "} + + {currentDiskStorage ? ( + <>{currentDiskStorage} GB + ) : ( + <>{currentSessionClass?.default_storage} GB (default) + )} + +
+
+ + +
+ {currentDiskStorage != null && ( + <> + + { + onChangeDiskStorage(event.target.valueAsNumber); + }} + /> + + GB + + + Gigabytes + + + + Default: {currentSessionClass?.default_storage} GB, max:{" "} + {currentSessionClass?.max_storage} GB + + + )} +
+ )} (false); const setResourceClass = useCallback( - (envClass: ResourceClass) => { + (envClass: ResourceClass, diskStorage: number | undefined) => { if (envClass) { dispatch( startSessionOptionsV2Slice.actions.setSessionClass(envClass.id) ); dispatch( startSessionOptionsV2Slice.actions.setStorage( - envClass.default_storage + diskStorage ?? envClass.default_storage ) ); setIsPendingResourceClass(false); @@ -86,7 +86,7 @@ export default function useSessionResourceClass({ } if (initialSessionClass && !isCustomLaunch) - setResourceClass(initialSessionClass); + setResourceClass(initialSessionClass, launcher.disk_storage); }, [ isCustomLaunch, isLoadingLauncherClass, From 88628472a582d6be2c09d854108fe12f13c96552 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 23 Dec 2024 15:19:45 +0100 Subject: [PATCH 04/12] improve validation --- .../features/session/components/SessionsList.tsx | 4 +++- .../sessionsV2/SessionView/SessionView.tsx | 12 ++++++++++++ .../SessionForm/LauncherDetailsFields.tsx | 16 +++++++++++----- .../SessionModals/ModifyResourcesLauncher.tsx | 7 +++++++ .../SessionModals/SelectResourceClass.tsx | 10 +++++++++- .../sessionsV2/useSessionResourceClass.hook.ts | 6 +++++- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/client/src/features/session/components/SessionsList.tsx b/client/src/features/session/components/SessionsList.tsx index b040873521..245426a67f 100644 --- a/client/src/features/session/components/SessionsList.tsx +++ b/client/src/features/session/components/SessionsList.tsx @@ -340,7 +340,9 @@ export function SessionRowResourceRequests({ {entries.map(([key, value], index) => ( - {value} + + {value} {(key === "memory" || key === "storage") && "GB "} + {key !== "name" && key} {entries.length - 1 === index ? " " : " | "} diff --git a/client/src/features/sessionsV2/SessionView/SessionView.tsx b/client/src/features/sessionsV2/SessionView/SessionView.tsx index 882a4dd0fc..8ef89ee53e 100644 --- a/client/src/features/sessionsV2/SessionView/SessionView.tsx +++ b/client/src/features/sessionsV2/SessionView/SessionView.tsx @@ -407,6 +407,18 @@ export function SessionView({ You do not have access to this resource class.

)} + {launcher && + launcherResourceClass && + launcher.disk_storage && + launcher.disk_storage > launcherResourceClass.max_storage && ( +

+ + The selected disk storage exceeds the maximum value allowed ( + {launcherResourceClass.max_storage} GB). +

+ )} {launcher && ( ( + render={({ + field: { ref, onChange, value, ...rest }, + fieldState: { error }, + }) => ( <>
onChange(value ? undefined : MIN_SESSION_STORAGE_GB) } @@ -178,9 +180,10 @@ export function LauncherDetailsFields({ control }: LauncherDetailsFieldsProps) { <> - Default: {watchCurrentSessionClass?.default_storage} GB, - max: {watchCurrentSessionClass?.max_storage} GB + Default: {watchCurrentSessionClass.default_storage} GB, + max: {watchCurrentSessionClass.max_storage} GB )} )} + rules={{ + max: watchCurrentSessionClass.max_storage, + }} />
)} diff --git a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx index 7feed0b0c6..2f63dc9ba1 100644 --- a/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/ModifyResourcesLauncher.tsx @@ -190,6 +190,10 @@ export function ModifyResourcesLauncherModal({ <> currentSessionClass.max_storage && + "is-invalid" + )} type="number" min={MIN_SESSION_STORAGE_GB} max={currentSessionClass?.max_storage} @@ -227,6 +231,9 @@ export function ModifyResourcesLauncherModal({ !resourcePools || resourcePools.length == 0 || isErrorResources || + (currentSessionClass != null && + currentDiskStorage != null && + currentDiskStorage > currentSessionClass?.max_storage) || !isDirty } onClick={onModifyResources} diff --git a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx index ef17143114..5d55308406 100644 --- a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx @@ -188,6 +188,10 @@ export function SelectResourceClassModal({ <> currentSessionClass.max_storage && + "is-invalid" + )} type="number" min={MIN_SESSION_STORAGE_GB} max={currentSessionClass?.max_storage} @@ -224,7 +228,11 @@ export function SelectResourceClassModal({
); } + +interface SelectResourceClassForm { + resourceClass: ResourceClass | undefined; + diskStorage: number | undefined; +} From 3a6c82a1edfb9690c66e12ab5998c8c5999cc3c7 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 13 Jan 2025 17:01:43 +0100 Subject: [PATCH 12/12] cleanup --- .../SessionModals/SelectResourceClass.tsx | 64 ------------------- 1 file changed, 64 deletions(-) diff --git a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx index 7c97f42398..6abed19b13 100644 --- a/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx +++ b/client/src/features/sessionsV2/components/SessionModals/SelectResourceClass.tsx @@ -245,70 +245,6 @@ export function SelectResourceClassModal({ /> )} - - {/* {currentSessionClass && ( -
-
- Disk Storage:{" "} - - {currentDiskStorage ? ( - <>{currentDiskStorage} GB - ) : ( - <>{currentSessionClass?.default_storage} GB (default) - )} - -
-
- - -
- {currentDiskStorage != null && ( - <> - currentSessionClass.max_storage && - "is-invalid" - )} - > - currentSessionClass.max_storage && - "is-invalid" - )} - type="number" - min={MIN_SESSION_STORAGE_GB} - max={currentSessionClass?.max_storage} - step={STEP_SESSION_STORAGE_GB} - value={currentDiskStorage} - onChange={(event) => { - onChangeDiskStorage(event.target.valueAsNumber); - }} - /> - - GB - - - Gigabytes - - - - Default: {currentSessionClass?.default_storage} GB, max:{" "} - {currentSessionClass?.max_storage} GB - -
- Selected disk storage exceeds maximum allowed value ( - {currentSessionClass.max_storage} GB). -
- - )} -
- )} */}