Skip to content

Commit

Permalink
feat: Resource pool tab for workspace (determined-ai#7582)
Browse files Browse the repository at this point in the history
  • Loading branch information
gt2345 authored Aug 11, 2023
1 parent 369ddf3 commit bbe70e0
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 42 deletions.
16 changes: 16 additions & 0 deletions harness/determined/common/api/bindings.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions master/internal/api_workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,12 @@ func (a *apiServer) PatchWorkspace(
insertColumns = append(insertColumns, "uid", "user_", "gid", "group_")
}

if req.Workspace.DefaultComputePool != "" {
updatedWorkspace.DefaultComputePool = req.Workspace.DefaultComputePool
if req.Workspace.DefaultComputeResourcePool != nil {
updatedWorkspace.DefaultComputePool = *req.Workspace.DefaultComputeResourcePool
insertColumns = append(insertColumns, "default_compute_pool")
}
if req.Workspace.DefaultAuxPool != "" {
updatedWorkspace.DefaultAuxPool = req.Workspace.DefaultAuxPool
if req.Workspace.DefaultAuxResourcePool != nil {
updatedWorkspace.DefaultAuxPool = *req.Workspace.DefaultAuxResourcePool
insertColumns = append(insertColumns, "default_aux_pool")
}

Expand Down
53 changes: 45 additions & 8 deletions proto/pkg/workspacev1/workspace.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions proto/src/determined/workspace/v1/workspace.proto
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ message PatchWorkspace {
// Expects same format as experiment config's checkpoint storage.
optional google.protobuf.Struct checkpoint_storage_config = 13;
// Name of the default compute pool.
string default_compute_pool = 14;
string default_compute_pool = 14 [deprecated = true];
// Name of the default compute pool can be optional.
optional string default_compute_resource_pool = 16;
// Name of the default aux pool.
string default_aux_pool = 15;
string default_aux_pool = 15 [deprecated = true];
// Name of the default aux pool can be optional.
optional string default_aux_resource_pool = 17;
}
42 changes: 20 additions & 22 deletions webui/react/src/components/ResourcePoolCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import gcpLogo from 'assets/images/gcp-logo.svg?url';
import k8sLogo from 'assets/images/k8s-logo.svg?url';
import staticLogo from 'assets/images/on-prem-logo.svg?url';
import Card from 'components/kit/Card';
import { MenuItem } from 'components/kit/Dropdown';
import Icon from 'components/kit/Icon';
import { useModal } from 'components/kit/Modal';
import Spinner from 'components/kit/Spinner';
import SlotAllocationBar from 'components/SlotAllocationBar';
import { V1ResourcePoolTypeToLabel, V1SchedulerTypeToLabel } from 'constants/states';
import useFeature from 'hooks/useFeature';
import usePermissions from 'hooks/usePermissions';
import { paths } from 'routes/utils';
import { V1ResourcePoolType, V1RPQueueStat, V1SchedulerType } from 'services/api-ts-sdk';
import { maxPoolSlotCapacity } from 'stores/cluster';
Expand All @@ -27,14 +28,15 @@ import { useObservable } from 'utils/observable';
import { DarkLight } from 'utils/themes';

import Json from './Json';
import { useModal } from './kit/Modal';
import ResourcePoolBindingModalComponent from './ResourcePoolBindingModal';
import css from './ResourcePoolCard.module.scss';

interface Props {
actionMenu?: MenuItem[];
poolStats?: V1RPQueueStat | undefined;
resourcePool: ResourcePool;
size?: ShirtSize;
descriptiveLabel?: string;
}

const poolAttributes = [
Expand Down Expand Up @@ -88,12 +90,16 @@ export const PoolLogo: React.FC<{ type: V1ResourcePoolType }> = ({ type }) => {
return <img className={css['rp-type-logo']} src={iconSrc} />;
};

const ResourcePoolCard: React.FC<Props> = ({ resourcePool: pool }: Props) => {
const ResourcePoolCard: React.FC<Props> = ({
resourcePool: pool,
actionMenu,
descriptiveLabel,
}: Props) => {
const rpBindingFlagOn = useFeature().isOn('rp_binding');
const ResourcePoolBindingModal = useModal(ResourcePoolBindingModalComponent);

const descriptionClasses = [css.description];
const { canManageResourcePoolBindings } = usePermissions();

const resourcePoolBindingMap = useObservable(clusterStore.resourcePoolBindings);
const resourcePoolBindings: number[] = resourcePoolBindingMap.get(pool.name, []);
const workspaces = Loadable.getOrElse([], useObservable(workspaceStore.workspaces));
Expand Down Expand Up @@ -132,9 +138,14 @@ const ResourcePoolCard: React.FC<Props> = ({ resourcePool: pool }: Props) => {
}, {} as JsonObject);
}, [processedPool, isAux, pool]);

const onDropdown = useCallback(() => {
ResourcePoolBindingModal.open();
}, [ResourcePoolBindingModal]);
const onDropdown = useCallback(
(key: string) => {
if (key === 'bindings') {
ResourcePoolBindingModal.open();
}
},
[ResourcePoolBindingModal],
);

const onSaveBindings = useCallback(
(bindings: string[]) => {
Expand All @@ -147,18 +158,7 @@ const ResourcePoolCard: React.FC<Props> = ({ resourcePool: pool }: Props) => {
return (
<>
<Card
actionMenu={
rpBindingFlagOn && canManageResourcePoolBindings
? [
{
disabled: pool.defaultAuxPool || pool.defaultComputePool,
icon: <Icon name="four-squares" title="manage-bindings" />,
key: 'bindings',
label: 'Manage bindings',
},
]
: []
}
actionMenu={actionMenu}
href={paths.resourcePool(pool.name)}
size="medium"
onDropdown={onDropdown}>
Expand All @@ -168,9 +168,7 @@ const ResourcePoolCard: React.FC<Props> = ({ resourcePool: pool }: Props) => {
<div className={css.name}>{pool.name}</div>
</div>
<div className={css.default}>
{(pool.defaultAuxPool && pool.defaultComputePool && <span>Default</span>) ||
(pool.defaultComputePool && <span>Default Compute</span>) ||
(pool.defaultAuxPool && <span>Default Aux</span>)}
<span>{descriptiveLabel}</span>
{pool.description && <Icon name="info" showTooltip title={pool.description} />}
</div>
</div>
Expand Down
35 changes: 34 additions & 1 deletion webui/react/src/pages/Clusters/ClustersOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React, { useCallback, useState } from 'react';

import Card from 'components/kit/Card';
import Icon from 'components/kit/Icon';
import ResourcePoolCard from 'components/ResourcePoolCard';
import ResourcePoolDetails from 'components/ResourcePoolDetails';
import Section from 'components/Section';
import useFeature from 'hooks/useFeature';
import usePermissions from 'hooks/usePermissions';
import clusterStore from 'stores/cluster';
import { ResourcePool } from 'types';
import { Loadable } from 'utils/loadable';
Expand All @@ -14,19 +17,49 @@ import { ClusterOverallStats } from '../Cluster/ClusterOverallStats';

const ClusterOverview: React.FC = () => {
const resourcePools = useObservable(clusterStore.resourcePools);
const rpBindingFlagOn = useFeature().isOn('rp_binding');
const { canManageResourcePoolBindings } = usePermissions();

const [rpDetail, setRpDetail] = useState<ResourcePool>();

const hideModal = useCallback(() => setRpDetail(undefined), []);

const actionMenu = useCallback(
(pool: ResourcePool) =>
rpBindingFlagOn && canManageResourcePoolBindings
? [
{
disabled: pool.defaultAuxPool || pool.defaultComputePool,
icon: <Icon name="four-squares" title="manage-bindings" />,
key: 'bindings',
label: 'Manage bindings',
},
]
: undefined,
[canManageResourcePoolBindings, rpBindingFlagOn],
);

const renderDefaultLabel = useCallback((pool: ResourcePool) => {
if (pool.defaultAuxPool && pool.defaultComputePool) return 'Default';
if (pool.defaultComputePool) return 'Default Aux';
if (pool.defaultAuxPool) return 'Default Compute';
}, []);

return (
<>
<ClusterOverallStats />
<ClusterOverallBar />
<Section title="Resource Pools">
<Card.Group size="medium">
{Loadable.isLoaded(resourcePools) &&
resourcePools.data.map((rp, idx) => <ResourcePoolCard key={idx} resourcePool={rp} />)}
resourcePools.data.map((rp) => (
<ResourcePoolCard
actionMenu={actionMenu(rp)}
descriptiveLabel={renderDefaultLabel(rp)}
key={rp.name}
resourcePool={rp}
/>
))}
</Card.Group>
</Section>
{!!rpDetail && (
Expand Down
Loading

0 comments on commit bbe70e0

Please sign in to comment.