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

use all clusterqueues #3540

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { Bullseye, Alert, Spinner } from '@patternfly/react-core';
import { ClusterQueueKind, LocalQueueKind, WorkloadKind } from '~/k8sTypes';
import { FetchStateObject } from '~/types';
import { DEFAULT_LIST_FETCH_STATE, DEFAULT_VALUE_FETCH_STATE } from '~/utilities/const';
import { DEFAULT_LIST_FETCH_STATE } from '~/utilities/const';
import { SupportedArea, conditionalArea } from '~/concepts/areas';
import useSyncPreferredProject from '~/concepts/projects/useSyncPreferredProject';
import { ProjectsContext, byName } from '~/concepts/projects/ProjectsContext';
Expand All @@ -20,13 +20,14 @@ import useLocalQueues from './useLocalQueues';
import useWorkloads from './useWorkloads';

type DistributedWorkloadsContextType = {
clusterQueue: FetchStateObject<ClusterQueueKind | undefined>;
clusterQueues: FetchStateObject<ClusterQueueKind[]>;
localQueues: FetchStateObject<LocalQueueKind[]>;
workloads: FetchStateObject<WorkloadKind[]>;
projectCurrentMetrics: DWProjectCurrentMetrics;
refreshAllData: () => void;
namespace: string;
projectDisplayName: string;
cqExists: boolean;
};

type DistributedWorkloadsContextProviderProps = {
Expand All @@ -35,13 +36,14 @@ type DistributedWorkloadsContextProviderProps = {
};

export const DistributedWorkloadsContext = React.createContext<DistributedWorkloadsContextType>({
clusterQueue: DEFAULT_VALUE_FETCH_STATE,
clusterQueues: DEFAULT_LIST_FETCH_STATE,
localQueues: DEFAULT_LIST_FETCH_STATE,
workloads: DEFAULT_LIST_FETCH_STATE,
projectCurrentMetrics: DEFAULT_DW_PROJECT_CURRENT_METRICS,
refreshAllData: () => undefined,
namespace: '',
projectDisplayName: '',
cqExists: false,
});

export const DistributedWorkloadsContextProvider =
Expand All @@ -59,17 +61,23 @@ export const DistributedWorkloadsContextProvider =

// TODO mturley implement lazy loading, let the context consumers tell us what data they need and make the other ones throw a NotReadyError

const clusterQueues = useMakeFetchObject<ClusterQueueKind[]>(useClusterQueues(refreshRate));
// We only support one ClusterQueue, but if the user has created multiple we use the first one with resourceGroups
const clusterQueue: FetchStateObject<ClusterQueueKind | undefined> = {
...clusterQueues,
data: clusterQueues.data.find((cq) => cq.spec.resourceGroups?.length),
};

const localQueues = useMakeFetchObject<LocalQueueKind[]>(
useLocalQueues(namespace, refreshRate),
);

const allClusterQueues = useMakeFetchObject<ClusterQueueKind[]>(useClusterQueues(refreshRate));

const validCQExists = allClusterQueues.data.some((cq) => cq.spec.resourceGroups?.length);

const clusterQueues = {
...allClusterQueues,
data: allClusterQueues.data.filter(
(cq) =>
localQueues.data.some((lq) => lq.spec.clusterQueue === cq.metadata?.name) &&
cq.spec.resourceGroups?.length,
),
};

const workloads = useMakeFetchObject<WorkloadKind[]>(useWorkloads(namespace, refreshRate));

const projectCurrentMetrics = useDWProjectCurrentMetrics(
Expand Down Expand Up @@ -115,13 +123,14 @@ export const DistributedWorkloadsContextProvider =
return (
<DistributedWorkloadsContext.Provider
value={{
clusterQueue,
clusterQueues,
localQueues,
workloads,
projectCurrentMetrics,
refreshAllData,
namespace,
projectDisplayName: getDisplayNameFromK8sResource(project),
cqExists: validCQExists,
}}
>
{children}
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/concepts/distributedWorkloads/__tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,20 @@ describe('getQueueRequestedResources', () => {

describe('getTotalSharedQuota', () => {
it('correctly parses and adds up total resources from clusterQueue resourceGroups', () => {
const mockClusterQueue = mockClusterQueueK8sResource({});
expect(getTotalSharedQuota(mockClusterQueue)).toEqual({
const mockClusterQueues = [mockClusterQueueK8sResource({})];
expect(getTotalSharedQuota(mockClusterQueues)).toEqual({
cpuCoresRequested: 100,
memoryBytesRequested: 68719476736,
} satisfies WorkloadRequestedResources);
});
it('correctley parses and adds up total resources from multiple clusterQueues', () => {
const mockClusterQueues = [
mockClusterQueueK8sResource({ name: 'test-clusterqueue-1' }),
mockClusterQueueK8sResource({ name: 'test-clusterqueue-2' }),
];
expect(getTotalSharedQuota(mockClusterQueues)).toEqual({
cpuCoresRequested: 200,
memoryBytesRequested: 137438953472,
} satisfies WorkloadRequestedResources);
});
});
30 changes: 16 additions & 14 deletions frontend/src/concepts/distributedWorkloads/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,22 +241,24 @@ export const getQueueRequestedResources = (
};

export const getTotalSharedQuota = (
clusterQueue?: ClusterQueueKind,
clusterQueues?: ClusterQueueKind[],
): WorkloadRequestedResources => {
const sumFromResourceGroups = (units: UnitOption[], attribute: ContainerResourceAttributes) =>
(clusterQueue?.spec.resourceGroups || []).reduce(
(resourceGroupsTotal, resourceGroup) =>
resourceGroupsTotal +
resourceGroup.flavors.reduce((flavorsTotal, flavor) => {
const [value, unit] = convertToUnit(
String(flavor.resources.find(({ name }) => name === attribute)?.nominalQuota || 0),
units,
'',
);
return unit.unit === '' ? flavorsTotal + value : flavorsTotal;
}, 0),
0,
);
(clusterQueues || [])
.flatMap((clusterQueue) => clusterQueue.spec.resourceGroups || [])
.reduce(
(resourceGroupsTotal, resourceGroup) =>
resourceGroupsTotal +
resourceGroup.flavors.reduce((flavorsTotal, flavor) => {
const [value, unit] = convertToUnit(
String(flavor.resources.find(({ name }) => name === attribute)?.nominalQuota || 0),
units,
'',
);
return unit.unit === '' ? flavorsTotal + value : flavorsTotal;
}, 0),
0,
);
return {
cpuCoresRequested: sumFromResourceGroups(CPU_UNITS, ContainerResourceAttributes.CPU),
memoryBytesRequested: sumFromResourceGroups(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const GlobalDistributedWorkloadsTabs: React.FC<GlobalDistributedWorkloadsTabsPro
const tabs = useDistributedWorkloadsTabs();
const activeTab = tabs.find(({ id }) => id === activeTabId);
const { namespace } = useParams<{ namespace: string }>();
const { clusterQueue, localQueues } = React.useContext(DistributedWorkloadsContext);
const requiredFetches = [clusterQueue, localQueues];
const { clusterQueues, localQueues, cqExists } = React.useContext(DistributedWorkloadsContext);
const requiredFetches = [clusterQueues, localQueues];
const error = requiredFetches.find((f) => !!f.error)?.error;
const loaded = requiredFetches.every((f) => f.loaded);

Expand All @@ -51,9 +51,9 @@ const GlobalDistributedWorkloadsTabs: React.FC<GlobalDistributedWorkloadsTabsPro
return <LoadingState />;
}

if (!clusterQueue.data || localQueues.data.length === 0) {
const nonAdmin = !clusterQueue.data;
const title = `Configure the ${!clusterQueue.data ? 'cluster queue' : 'project queue'}`;
if (clusterQueues.data.length === 0 || localQueues.data.length === 0) {
const nonAdmin = !cqExists;
const title = `Configure the ${!cqExists ? 'cluster queue' : 'project queue'}`;
const message = nonAdmin
? 'Ask your cluster admin to configure the cluster queue.'
: 'Configure the queue for this project, or select a different project.';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { LoadingState } from '~/pages/distributedWorkloads/components/LoadingSta
import { RequestedResourcesBulletChart } from './RequestedResourcesBulletChart';

export const RequestedResources: React.FC = () => {
const { localQueues, clusterQueue } = React.useContext(DistributedWorkloadsContext);
const requiredFetches = [localQueues, clusterQueue];
const { localQueues, clusterQueues } = React.useContext(DistributedWorkloadsContext);
const requiredFetches = [localQueues, clusterQueues];
const error = requiredFetches.find((f) => !!f.error)?.error;
const loaded = requiredFetches.every((f) => f.loaded);

Expand All @@ -25,8 +25,8 @@ export const RequestedResources: React.FC = () => {
}

const requestedByThisProject = getQueueRequestedResources(localQueues.data);
const requestedByAllProjects = getQueueRequestedResources([clusterQueue.data]);
const totalSharedQuota = getTotalSharedQuota(clusterQueue.data);
const requestedByAllProjects = getQueueRequestedResources(clusterQueues.data);
const totalSharedQuota = getTotalSharedQuota(clusterQueues.data);

return (
<CardBody>
Expand Down
Loading