fix(app): surface placement rules from form [EE-6553] (#11816)

pull/10848/head
Ali 2024-05-14 13:34:06 +12:00 committed by GitHub
parent 1ba4b590f4
commit b6daee2850
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 89 additions and 3 deletions

View File

@ -27,6 +27,7 @@ import { ApplicationAutoScalingTable } from './ApplicationAutoScalingTable';
import { ApplicationEnvVarsTable } from './ApplicationEnvVarsTable';
import { ApplicationVolumeConfigsTable } from './ApplicationVolumeConfigsTable';
import { ApplicationPersistentDataTable } from './ApplicationPersistentDataTable';
import { PlacementsTable } from './PlacementsTable';
export function ApplicationDetailsWidget() {
const stateAndParams = useCurrentStateAndParams();
@ -140,6 +141,7 @@ export function ApplicationDetailsWidget() {
appName={name}
app={app}
/>
{!externalApp && <PlacementsTable app={app} />}
</WidgetBody>
</Widget>
</div>

View File

@ -0,0 +1,84 @@
import { Minimize2 } from 'lucide-react';
import { NodeSelectorRequirement, Pod } from 'kubernetes-types/core/v1';
import { useMemo } from 'react';
import { Icon } from '@@/Icon';
import { TextTip } from '@@/Tip/TextTip';
import { Application } from '../../types';
import { applicationIsKind } from '../../utils';
type Props = {
app?: Application;
};
export function PlacementsTable({ app }: Props) {
const formPlacements = useAppPlacements(app);
return (
<>
<div className="text-muted mb-4 mt-6 flex items-center">
<Icon icon={Minimize2} className="!mr-2" />
Placement preferences and constraints
</div>
{!formPlacements.length && (
<TextTip color="blue">
This application has no pod preference or constraint rules from the
application form. See the application YAML for other placement rules.
</TextTip>
)}
{formPlacements.length > 0 && (
<table className="table">
<thead>
<tr className="text-muted">
<td className="w-1/3">Key</td>
<td className="w-2/3">Value(s)</td>
</tr>
</thead>
<tbody>
{formPlacements.map((placement, i) => (
<tr key={i}>
<td>{placement.key}</td>
<td>{placement.values?.join(', ')}</td>
</tr>
))}
</tbody>
</table>
)}
</>
);
}
// useAppPlacements is a custom hook that returns the placements that relate to the Portainer application form.
function useAppPlacements(app?: Application): NodeSelectorRequirement[] {
const formPlacements = useMemo(() => {
if (!app) {
return [];
}
// firstly get the pod spec
const podSpec = applicationIsKind<Pod>('Pod', app)
? app.spec
: app.spec?.template?.spec;
// secondly filter all placements to get the placements that are related to the Portainer form. They are:
// - preference (s) in spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution
const placements =
podSpec?.affinity?.nodeAffinity
?.preferredDuringSchedulingIgnoredDuringExecution;
// - matchExpressions in preference
const placementsWithMatchExpressions = placements?.filter(
(placement) => placement?.preference?.matchExpressions
);
// - only matchExpressions items with the operator: In
const portainerPlacements =
placementsWithMatchExpressions?.flatMap(
(placement) =>
placement?.preference?.matchExpressions?.filter(
(expression) => expression?.operator === 'In'
) || []
) || [];
return portainerPlacements;
}, [app]);
return formPlacements;
}

View File

@ -36,7 +36,7 @@ export function PlacementsDatatable({
<ExpandableDatatable
isLoading={isLoading}
getRowCanExpand={(row) => !row.original.acceptsApplication}
title="Placement constraints/preferences"
title="Nodes vs. placement constraints/preferences"
titleIcon={Minimize2}
dataset={dataset}
settingsManager={tableState}
@ -51,8 +51,8 @@ export function PlacementsDatatable({
</TextTip>
) : (
<TextTip color="blue">
The placement table helps you understand whether or not this
application can be deployed on a specific node.
The table below shows whether or not this application can be
deployed on the nodes listed.
</TextTip>
)
}