Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: configure disk storage for Renku 2.0 sessions #3463

Merged
merged 15 commits into from
Jan 14, 2025
4 changes: 3 additions & 1 deletion client/src/features/session/components/SessionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ export function SessionRowResourceRequests({
{entries.map(([key, value], index) => (
<span key={key}>
<span className="text-nowrap">
<span className="fw-bold">{value} </span>
<span className="fw-bold">
{value} {(key === "memory" || key === "storage") && "GB "}
</span>
{key !== "name" && key}
</span>
{entries.length - 1 === index ? " " : " | "}
Expand Down
76 changes: 47 additions & 29 deletions client/src/features/sessionsV2/SessionView/SessionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@ export function SessionView({
resourceRequests={{
name: launcherResourceClass.name,
cpu: launcherResourceClass.cpu,
memory: `${launcherResourceClass.memory}G`,
storage: `${launcherResourceClass.default_storage}G`,
memory: launcherResourceClass.memory,
storage:
launcher?.disk_storage ?? launcherResourceClass.default_storage,
gpu: launcherResourceClass.gpu,
}}
/>
Expand Down Expand Up @@ -374,27 +375,29 @@ export function SessionView({
<div>
<div className={cx("d-flex", "justify-content-between", "mb-2")}>
<h4 className="my-auto">Default Resource Class</h4>
<PermissionsGuard
disabled={null}
enabled={
<>
<Button
color="outline-primary"
id="modify-resource-class-button"
onClick={toggleModifyResources}
size="sm"
tabIndex={0}
>
<Pencil className="bi" />
</Button>
<UncontrolledTooltip target="modify-resource-class-button">
Set resource class
</UncontrolledTooltip>
</>
}
requestedPermission="write"
userPermissions={permissions}
/>
{launcher && (
<PermissionsGuard
disabled={null}
enabled={
<>
<Button
color="outline-primary"
id="modify-resource-class-button"
onClick={toggleModifyResources}
size="sm"
tabIndex={0}
>
<Pencil className="bi" />
</Button>
<UncontrolledTooltip target="modify-resource-class-button">
Set resource class
</UncontrolledTooltip>
</>
}
requestedPermission="write"
userPermissions={permissions}
/>
)}
</div>
{resourceDetails}
{launcherResourceClass && !userLauncherResourceClass && (
Expand All @@ -403,12 +406,27 @@ export function SessionView({
You do not have access to this resource class.
</p>
)}
<ModifyResourcesLauncherModal
isOpen={isModifyResourcesOpen}
toggleModal={toggleModifyResources}
resourceClassId={userLauncherResourceClass?.id}
sessionLauncherId={launcher?.id}
/>
{launcher &&
launcherResourceClass &&
launcher.disk_storage &&
launcher.disk_storage > launcherResourceClass.max_storage && (
<p>
<ExclamationTriangleFill
className={cx("bi", "text-warning", "me-1")}
/>
The selected disk storage exceeds the maximum value allowed (
{launcherResourceClass.max_storage} GB).
</p>
)}
{launcher && (
<ModifyResourcesLauncherModal
isOpen={isModifyResourcesOpen}
toggleModal={toggleModifyResources}
resourceClassId={userLauncherResourceClass?.id}
diskStorage={launcher.disk_storage}
sessionLauncherId={launcher.id}
/>
)}
</div>

<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import cx from "classnames";
import { useMemo } from "react";
import { Control, Controller, useWatch } from "react-hook-form";
import {
Control,
Controller,
FieldErrors,
UseFormSetValue,
} from "react-hook-form";
import { SingleValue } from "react-select";
import { Input, Label } from "reactstrap";
FormText,
Input,
InputGroup,
InputGroupText,
Label,
UncontrolledTooltip,
} from "reactstrap";
import { WarnAlert } from "../../../../components/Alert";
import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert";
import { Loader } from "../../../../components/Loader";
import { useGetResourcePoolsQuery } from "../../../dataServices/computeResources.api";
import { ResourceClass } from "../../../dataServices/dataServices.types";
import { SessionClassSelectorV2 } from "../../../session/components/options/SessionClassOption";
import { SessionLauncherForm } from "../../sessionsV2.types";
import {
MIN_SESSION_STORAGE_GB,
STEP_SESSION_STORAGE_GB,
} from "../../../session/startSessionOptions.constants";

interface LauncherDetailsFieldsProps {
control: Control<SessionLauncherForm, unknown>;
errors: FieldErrors<SessionLauncherForm>;
setValue: UseFormSetValue<SessionLauncherForm>;
control: Control<SessionLauncherForm>;
}
export function LauncherDetailsFields({
setValue,
control,
errors,
}: LauncherDetailsFieldsProps) {
export function LauncherDetailsFields({ control }: LauncherDetailsFieldsProps) {
const {
data: resourcePools,
isLoading: isLoadingResourcesPools,
error: resourcePoolsError,
} = useGetResourcePoolsQuery({});

const onChangeResourceClass = (resourceClass: SingleValue<ResourceClass>) => {
if (resourceClass) setValue("resourceClass", resourceClass);
};

const defaultSessionClass = useMemo(
() =>
resourcePools
Expand All @@ -64,6 +59,13 @@ export function LauncherDetailsFields({
[resourcePools]
);

const watchCurrentSessionClass = useWatch({
control,
name: "resourceClass",
defaultValue: defaultSessionClass,
});
const watchCurrentDiskStorage = useWatch({ control, name: "diskStorage" });

return (
<div className={cx("d-flex", "flex-column", "gap-3")}>
<div className={cx("form-label", "mb-0")}>
Expand All @@ -76,9 +78,9 @@ export function LauncherDetailsFields({
<Controller
control={control}
name="name"
render={({ field }) => (
render={({ field, fieldState: { error } }) => (
<Input
className={cx(errors.name && "is-invalid")}
className={cx(error && "is-invalid")}
id="addSessionLauncherName"
placeholder="session name"
type="text"
Expand Down Expand Up @@ -107,32 +109,112 @@ export function LauncherDetailsFields({
{!isLoadingResourcesPools &&
resourcePools &&
resourcePools?.length > 0 ? (
<Controller
control={control}
name="resourceClass"
defaultValue={defaultSessionClass}
render={() => (
<>
<SessionClassSelectorV2
id="addSessionResourceClass"
resourcePools={resourcePools}
onChange={onChangeResourceClass}
defaultSessionClass={defaultSessionClass}
/>
{errors?.resourceClass && (
<div className={cx("small", "text-danger")}>
Please provide a resource class
</div>
)}
</>
)}
rules={{ required: true }}
/>
<>
<Controller
control={control}
name="resourceClass"
defaultValue={defaultSessionClass}
render={({
field: { onChange, value },
fieldState: { error },
}) => (
<>
<SessionClassSelectorV2
id="addSessionResourceClass"
currentSessionClass={value}
resourcePools={resourcePools}
onChange={onChange}
defaultSessionClass={defaultSessionClass}
/>
{error && (
<div className={cx("small", "text-danger")}>
Please provide a resource class
</div>
)}
</>
)}
rules={{ required: true }}
/>
</>
) : (
<WarnAlert>
There are no one resource pool available to create a session
</WarnAlert>
)}

{watchCurrentSessionClass && (
<div className={cx("field-group", "mt-3")}>
<div>
Disk Storage:{" "}
<span className="fw-bold">
{watchCurrentDiskStorage ? (
<>{watchCurrentDiskStorage} GB</>
) : (
<>{watchCurrentSessionClass?.default_storage} GB (default)</>
)}
</span>
</div>
<Controller
control={control}
name="diskStorage"
render={({
field: { ref, onChange, value, ...rest },
fieldState: { error },
}) => (
<>
<div className={cx("form-check", "form-switch")}>
<Input
type="checkbox"
role="switch"
id="configure-disk-storage"
checked={watchCurrentDiskStorage != null}
onChange={() =>
onChange(
value
? undefined
: watchCurrentSessionClass.default_storage
)
}
/>
<Label for="configure-disk-storage">
Configure disk storage
</Label>
</div>
{watchCurrentDiskStorage != null && (
<>
<InputGroup>
<Input
className={cx(error && "is-invalid")}
type="number"
min={MIN_SESSION_STORAGE_GB}
max={watchCurrentSessionClass.max_storage}
step={STEP_SESSION_STORAGE_GB}
innerRef={ref}
onChange={onChange}
value={value}
{...rest}
/>
<InputGroupText id="configure-disk-storage-addon">
GB
</InputGroupText>
<UncontrolledTooltip target="configure-disk-storage-addon">
Gigabytes
</UncontrolledTooltip>
</InputGroup>
<FormText>
Default: {watchCurrentSessionClass.default_storage} GB,
max: {watchCurrentSessionClass.max_storage} GB
</FormText>
</>
)}
</>
)}
rules={{
max: watchCurrentSessionClass.max_storage,
}}
/>
</div>
)}
</div>
</div>
);
Expand Down
Loading
Loading