diff --git a/packages/api-v4/src/linodes/types.ts b/packages/api-v4/src/linodes/types.ts index f8c1abe5ee2..cfbd0dd089b 100644 --- a/packages/api-v4/src/linodes/types.ts +++ b/packages/api-v4/src/linodes/types.ts @@ -1,7 +1,7 @@ import type { Region } from '../regions'; import type { IPAddress, IPRange } from '../networking/types'; import type { SSHKey } from '../profile/types'; -import type { PlacementGroup } from '../placement-groups/types'; +import type { PlacementGroupPayload } from '../placement-groups/types'; export type Hypervisor = 'kvm' | 'zen'; @@ -24,11 +24,7 @@ export interface Linode { ipv4: string[]; ipv6: string | null; label: string; - // While the API returns an array of PlacementGroup objects for future proofing, - // we only support one PlacementGroup per Linode at this time, hence the tuple. - placement_groups: - | [Pick] - | []; + placement_group?: PlacementGroupPayload; // If not in a placement group, this will be excluded from the response. type: string | null; status: LinodeStatus; updated: string; @@ -344,8 +340,13 @@ export interface UserData { } export interface CreateLinodePlacementGroupPayload { - id?: number | null; - compliant_only?: boolean | null; + id: number; + /** + * This parameter is silent in Cloud Manager, but still needs to be represented in the API types. + * + * @default false + */ + compliant_only?: boolean; } export interface CreateLinodeRequest { diff --git a/packages/api-v4/src/placement-groups/placement-groups.ts b/packages/api-v4/src/placement-groups/placement-groups.ts index 16cd63182fb..123149c8589 100644 --- a/packages/api-v4/src/placement-groups/placement-groups.ts +++ b/packages/api-v4/src/placement-groups/placement-groups.ts @@ -17,7 +17,7 @@ import type { CreatePlacementGroupPayload, PlacementGroup, UnassignLinodesFromPlacementGroupPayload, - RenamePlacementGroupPayload, + UpdatePlacementGroupPayload, } from './types'; /** @@ -72,7 +72,7 @@ export const createPlacementGroup = (data: CreatePlacementGroupPayload) => */ export const renamePlacementGroup = ( placementGroupId: number, - data: RenamePlacementGroupPayload + data: UpdatePlacementGroupPayload ) => Request( setURL( diff --git a/packages/api-v4/src/placement-groups/types.ts b/packages/api-v4/src/placement-groups/types.ts index 63f64584004..df593e53bdc 100644 --- a/packages/api-v4/src/placement-groups/types.ts +++ b/packages/api-v4/src/placement-groups/types.ts @@ -6,6 +6,7 @@ export const AFFINITY_TYPES = { } as const; export type AffinityType = keyof typeof AFFINITY_TYPES; +export type AffinityEnforcement = 'Strict' | 'Flexible'; export interface PlacementGroup { id: number; @@ -13,25 +14,37 @@ export interface PlacementGroup { region: Region['id']; affinity_type: AffinityType; is_compliant: boolean; - linode_ids: number[]; + linodes: { + linode: number; + is_compliant: boolean; + }[]; + is_strict: boolean; } -// The `strict` parameter specifies whether placement groups should be ignored when looking for a host. -// TODO VM_Placement: figure out the values for each create flow (create, clone, migrate etc) -export type CreatePlacementGroupPayload = Pick< +export type PlacementGroupPayload = Pick< PlacementGroup, - 'label' | 'affinity_type' | 'region' -> & { strict: boolean }; + 'id' | 'label' | 'affinity_type' | 'is_strict' +>; -export type RenamePlacementGroupPayload = Pick; +export type CreatePlacementGroupPayload = Omit & { + region: Region['id']; +}; + +export type UpdatePlacementGroupPayload = Pick; /** * Since the API expects an array of ONE linode id, we'll use a tuple here. */ export type AssignLinodesToPlacementGroupPayload = { linodes: [number]; - strict: boolean; + /** + * This parameter is silent in Cloud Manager, but still needs to be represented in the API types. + * + * @default false + */ + compliant_only?: boolean; }; + export type UnassignLinodesFromPlacementGroupPayload = { linodes: [number]; }; diff --git a/packages/manager/.changeset/pr-10200-upcoming-features-1709050942048.md b/packages/manager/.changeset/pr-10200-upcoming-features-1709050942048.md new file mode 100644 index 00000000000..53a3d5f1c89 --- /dev/null +++ b/packages/manager/.changeset/pr-10200-upcoming-features-1709050942048.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Update Placement Groups types, methods and factories ([#10200](https://github.com/linode/manager/pull/10200)) diff --git a/packages/manager/src/__data__/linodes.ts b/packages/manager/src/__data__/linodes.ts index e0c5d2a608a..cf97f0af27d 100644 --- a/packages/manager/src/__data__/linodes.ts +++ b/packages/manager/src/__data__/linodes.ts @@ -24,7 +24,12 @@ export const linode1: Linode = { ipv4: ['97.107.143.78', '98.107.143.78', '99.107.143.78'], ipv6: '2600:3c03::f03c:91ff:fe0a:109a/64', label: 'test', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'pg-1', + }, region: 'us-east', specs: { disk: 20480, @@ -64,7 +69,12 @@ export const linode2: Linode = { ipv4: ['97.107.143.49'], ipv6: '2600:3c03::f03c:91ff:fe0a:0d7a/64', label: 'another-test', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'pg-1', + }, region: 'us-east', specs: { disk: 30720, @@ -104,7 +114,12 @@ export const linode3: Linode = { ipv4: ['97.107.143.49'], ipv6: '2600:3c03::f03c:91ff:fe0a:0d7a/64', label: 'another-test', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'pg-1', + }, region: 'us-east', specs: { disk: 30720, @@ -144,7 +159,12 @@ export const linode4: Linode = { ipv4: ['97.107.143.49'], ipv6: '2600:3c03::f03c:91ff:fe0a:0d7a/64', label: 'another-test-eu', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'pg-1', + }, region: 'eu-west', specs: { disk: 30720, diff --git a/packages/manager/src/factories/linodes.ts b/packages/manager/src/factories/linodes.ts index 64807d6cd23..62d5b55bb64 100644 --- a/packages/manager/src/factories/linodes.ts +++ b/packages/manager/src/factories/linodes.ts @@ -262,16 +262,11 @@ export const linodeFactory = Factory.Sync.makeFactory({ ipv4: ['50.116.6.212', '192.168.203.1'], ipv6: '2600:3c00::f03c:92ff:fee2:6c40/64', label: Factory.each((i) => `linode-${i}`), - placement_groups: [ - placementGroupFactory.build({ - affinity_type: 'anti_affinity', - id: 1, - is_compliant: true, - label: 'test', - linode_ids: [1], - region: 'us-east', - }), - ], + placement_group: placementGroupFactory.build({ + affinity_type: 'anti_affinity', + id: 1, + label: 'pg-1', + }), region: 'us-east', specs: linodeSpecsFactory.build(), status: 'running', diff --git a/packages/manager/src/factories/placementGroups.ts b/packages/manager/src/factories/placementGroups.ts index fb0d0a2a73c..820c189c9c6 100644 --- a/packages/manager/src/factories/placementGroups.ts +++ b/packages/manager/src/factories/placementGroups.ts @@ -11,16 +11,54 @@ export const placementGroupFactory = Factory.Sync.makeFactory({ affinity_type: 'anti_affinity', id: Factory.each((id) => id), is_compliant: Factory.each(() => pickRandom([true, false])), + is_strict: true, label: Factory.each((id) => `pg-${id}`), - linode_ids: [0, 1, 2, 3, 5, 6, 7, 8, 43], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + { + is_compliant: true, + linode: 2, + }, + { + is_compliant: true, + linode: 3, + }, + { + is_compliant: true, + linode: 5, + }, + { + is_compliant: true, + linode: 6, + }, + { + is_compliant: true, + linode: 7, + }, + { + is_compliant: true, + linode: 8, + }, + { + is_compliant: true, + linode: 9, + }, + { + is_compliant: false, + linode: 43, + }, + ], region: 'us-east', }); export const createPlacementGroupPayloadFactory = Factory.Sync.makeFactory( { affinity_type: 'anti_affinity', + is_strict: true, label: Factory.each((id) => `mock-pg-${id}`), region: pickRandom(['us-east', 'us-southeast', 'ca-central']), - strict: true, } ); diff --git a/packages/manager/src/features/Linodes/LinodesCreate/AddonsPanel.test.tsx b/packages/manager/src/features/Linodes/LinodesCreate/AddonsPanel.test.tsx index 54712f0649d..1f918e443db 100644 --- a/packages/manager/src/features/Linodes/LinodesCreate/AddonsPanel.test.tsx +++ b/packages/manager/src/features/Linodes/LinodesCreate/AddonsPanel.test.tsx @@ -62,7 +62,12 @@ const props: AddonsPanelProps = { ipv4: ['45.56.75.98'], ipv6: '2600:3c00::f03c:93ff:fe85:576d/128', label: 'test_instance', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'test', + }, region: 'us-central', specs: { disk: 51200, @@ -102,7 +107,12 @@ const props: AddonsPanelProps = { ipv4: ['192.168.139.183', '139.144.17.202'], ipv6: '2600:3c04::f03c:93ff:fe75:0612/128', label: 'debian-ca-central', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'test', + }, region: 'ca-central', specs: { disk: 25600, @@ -141,7 +151,12 @@ const props: AddonsPanelProps = { ipv4: ['45.79.74.95'], ipv6: '2600:3c01::f03c:93ff:fe75:e4f9/128', label: 'almalinux-us-west', - placement_groups: [], + placement_group: { + affinity_type: 'anti_affinity', + id: 1, + is_strict: true, + label: 'test', + }, region: 'us-west', specs: { disk: 25600, diff --git a/packages/manager/src/features/Linodes/LinodesCreate/LinodeCreate.tsx b/packages/manager/src/features/Linodes/LinodesCreate/LinodeCreate.tsx index a98f321cadb..5b4026bfa4b 100644 --- a/packages/manager/src/features/Linodes/LinodesCreate/LinodeCreate.tsx +++ b/packages/manager/src/features/Linodes/LinodesCreate/LinodeCreate.tsx @@ -838,8 +838,7 @@ export class LinodeCreate extends React.PureComponent< ); const placement_group_payload: CreateLinodePlacementGroupPayload = { - compliant_only: true, - id: this.props.placementGroupSelection?.id, + id: this.props.placementGroupSelection?.id ?? -1, }; // eslint-disable-next-line sonarjs/no-unused-collection diff --git a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx index 234c8c01ab6..083885f2282 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx @@ -42,7 +42,7 @@ describe('LinodeRow', () => { ipv6={linode.ipv6 || ''} key={`linode-row-${1}`} label={linode.label} - placement_groups={linode.placement_groups} + placement_group={linode.placement_group} region={linode.region} specs={linode.specs} status={linode.status} diff --git a/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx b/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx index 50bde0d77d4..ac316735313 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx @@ -46,7 +46,7 @@ export const ListView = (props: RenderLinodesProps) => { ipv6={linode.ipv6 || ''} key={`linode-row-${idx}`} label={linode.label} - placement_groups={linode.placement_groups} + placement_group={linode.placement_group} region={linode.region} specs={linode.specs} status={linode.status} diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.test.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.test.tsx index 657f3468b8e..7a387dfaef2 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.test.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.test.tsx @@ -87,7 +87,48 @@ describe('PlacementGroupsAssignLinodesDrawer', () => { }); queryMocks.useAssignLinodesToPlacementGroup.mockReturnValue( placementGroupFactory.build({ - linode_ids: [1, 2, 0, 1, 2, 3, 5, 6, 7, 8, 43, 11], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + { + is_compliant: true, + linode: 2, + }, + { + is_compliant: true, + linode: 3, + }, + { + is_compliant: true, + linode: 5, + }, + { + is_compliant: true, + linode: 6, + }, + { + is_compliant: true, + linode: 7, + }, + { + is_compliant: true, + linode: 8, + }, + { + is_compliant: true, + linode: 9, + }, + { + is_compliant: true, + linode: 43, + }, + { + is_compliant: true, + linode: 11, + }, + ], }) ); diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx index 9022f02a4ba..d3c4e0da5f6 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsAssignLinodesDrawer.tsx @@ -142,7 +142,6 @@ export const PlacementGroupsAssignLinodesDrawer = ( const payload: AssignLinodesToPlacementGroupPayload = { linodes: [selectedLinode.id], - strict: true, }; try { diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsCreateDrawer.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsCreateDrawer.tsx index b64f3a9cc69..6b2e80cbe58 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsCreateDrawer.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsCreateDrawer.tsx @@ -15,8 +15,8 @@ import { handleGeneralErrors, } from 'src/utilities/formikErrorUtils'; -import { PlacementGroupsDrawerContent } from './PlacementGroupsDrawerContent'; import { MAX_NUMBER_OF_PLACEMENT_GROUPS } from './constants'; +import { PlacementGroupsDrawerContent } from './PlacementGroupsDrawerContent'; import type { PlacementGroupDrawerFormikProps, @@ -57,9 +57,9 @@ export const PlacementGroupsCreateDrawer = ( enableReinitialize: true, initialValues: { affinity_type: '' as PlacementGroupDrawerFormikProps['affinity_type'], + is_strict: true, label: '', region: selectedRegionId ?? '', - strict: true, }, onSubmit( values: PlacementGroupDrawerFormikProps, diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsDeleteModal.test.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsDeleteModal.test.tsx index e1362e4fa66..66fff87a554 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsDeleteModal.test.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsDeleteModal.test.tsx @@ -90,7 +90,12 @@ describe('PlacementGroupsDeleteModal', () => { affinity_type: 'anti_affinity', id: 1, label: 'PG-to-delete', - linode_ids: [1], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + ], region: 'us-east', }), }); @@ -127,7 +132,7 @@ describe('PlacementGroupsDeleteModal', () => { affinity_type: 'anti_affinity', id: 1, label: 'PG-to-delete', - linode_ids: [], + linodes: [], }), }); diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsLinodes/PlacementGroupsLinodes.test.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsLinodes/PlacementGroupsLinodes.test.tsx index dd6d063c000..448ad4efb8d 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsLinodes/PlacementGroupsLinodes.test.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsLinodes/PlacementGroupsLinodes.test.tsx @@ -19,7 +19,12 @@ describe('PlacementGroupsLinodes', () => { it('features the linodes table, a filter field, a create button and a docs link', () => { const placementGroup = placementGroupFactory.build({ - linode_ids: [1], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + ], }); const { getByPlaceholderText, getByRole } = renderWithTheme( diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.test.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.test.tsx index 03d6a4e6f8e..aec5ec9c314 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.test.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.test.tsx @@ -14,7 +14,29 @@ describe('PlacementGroups Summary', () => { id: 3, is_compliant: true, label: 'pg-3', - linode_ids: [2, 4, 6, 8, 10], + linodes: [ + { + is_compliant: true, + linode: 2, + }, + { + is_compliant: true, + linode: 4, + }, + { + is_compliant: true, + linode: 6, + }, + { + is_compliant: true, + linode: 8, + }, + { + is_compliant: true, + linode: 10, + }, + ], + region: 'us-east', })} /> diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx index ba83d5c302d..cd0e56cd9e8 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsLanding/PlacementGroupsRow.test.tsx @@ -72,7 +72,12 @@ describe('PlacementGroupsLanding', () => { affinity_type: 'anti_affinity', is_compliant: false, label: 'group 1', - linode_ids: [1], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + ], region: 'us-east', })} handleDeletePlacementGroup={handleDeletePlacementGroupMock} diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsRenameDrawer.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsRenameDrawer.tsx index 72dd8d96668..1eb03f28b6a 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsRenameDrawer.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsRenameDrawer.tsx @@ -57,9 +57,9 @@ export const PlacementGroupsRenameDrawer = ( enableReinitialize: true, initialValues: { affinity_type: selectedPlacementGroup?.affinity_type as PlacementGroupDrawerFormikProps['affinity_type'], + is_strict: true, label: selectedPlacementGroup?.label ?? '', region: selectedPlacementGroup?.region ?? '', - strict: true, }, onSubmit(values, { setErrors, setStatus, setSubmitting }) { setHasFormBeenSubmitted(false); diff --git a/packages/manager/src/features/PlacementGroups/types.ts b/packages/manager/src/features/PlacementGroups/types.ts index c23c6be27ab..238dc86c105 100644 --- a/packages/manager/src/features/PlacementGroups/types.ts +++ b/packages/manager/src/features/PlacementGroups/types.ts @@ -1,7 +1,7 @@ import { CreatePlacementGroupPayload, PlacementGroup, - RenamePlacementGroupPayload, + UpdatePlacementGroupPayload, } from '@linode/api-v4'; export type PlacementGroupsDrawerPropsBase = { @@ -20,7 +20,7 @@ export type PlacementGroupsRenameDrawerProps = PlacementGroupsDrawerPropsBase & selectedPlacementGroup: PlacementGroup | undefined; }; -export type PlacementGroupDrawerFormikProps = RenamePlacementGroupPayload & +export type PlacementGroupDrawerFormikProps = UpdatePlacementGroupPayload & CreatePlacementGroupPayload; export type PlacementGroupsAssignLinodesDrawerProps = PlacementGroupsDrawerPropsBase & { diff --git a/packages/manager/src/features/PlacementGroups/utils.test.ts b/packages/manager/src/features/PlacementGroups/utils.test.ts index 0600821731b..b18bbbd8f08 100644 --- a/packages/manager/src/features/PlacementGroups/utils.test.ts +++ b/packages/manager/src/features/PlacementGroups/utils.test.ts @@ -2,6 +2,7 @@ import { placementGroupFactory, regionFactory } from 'src/factories'; import { affinityTypeOptions, + getAffinityEnforcement, getLinodesFromAllPlacementGroups, getPlacementGroupLinodeCount, hasPlacementGroupReachedCapacity, @@ -9,11 +10,26 @@ import { import type { PlacementGroup } from '@linode/api-v4'; +const initialLinodeData = [ + { + is_compliant: true, + linode: 1, + }, + { + is_compliant: true, + linode: 2, + }, + { + is_compliant: true, + linode: 3, + }, +]; + describe('getPlacementGroupLinodeCount', () => { it('returns the length of the linode_ids array', () => { expect( getPlacementGroupLinodeCount({ - linode_ids: [1, 2, 3], + linodes: initialLinodeData, } as PlacementGroup) ).toBe(3); }); @@ -37,7 +53,7 @@ describe('hasPlacementGroupReachedCapacity', () => { expect( hasPlacementGroupReachedCapacity({ placementGroup: placementGroupFactory.build({ - linode_ids: [1, 2, 3], + linodes: initialLinodeData, }), region: regionFactory.build({ maximum_vms_per_pg: 3, @@ -50,7 +66,7 @@ describe('hasPlacementGroupReachedCapacity', () => { expect( hasPlacementGroupReachedCapacity({ placementGroup: placementGroupFactory.build({ - linode_ids: [1, 2, 3], + linodes: initialLinodeData, }), region: regionFactory.build({ maximum_vms_per_pg: 4, @@ -64,8 +80,23 @@ describe('getLinodesFromAllPlacementGroups', () => { it('returns an array of unique linode ids from all placement groups', () => { expect( getLinodesFromAllPlacementGroups([ - { linode_ids: [1, 2, 3] }, - { linode_ids: [3, 4, 5] }, + { linodes: initialLinodeData }, + { + linodes: [ + { + is_compliant: true, + linode: 3, + }, + { + is_compliant: true, + linode: 4, + }, + { + is_compliant: true, + linode: 5, + }, + ], + }, ] as PlacementGroup[]) ).toEqual([1, 2, 3, 4, 5]); }); @@ -74,3 +105,13 @@ describe('getLinodesFromAllPlacementGroups', () => { expect(getLinodesFromAllPlacementGroups(undefined)).toEqual([]); }); }); + +describe('getAffinityEnforcement', () => { + it('returns "Strict" if `is_strict` is true', () => { + expect(getAffinityEnforcement(true)).toBe('Strict'); + }); + + it('returns "Flexible" if `is_strict` is false', () => { + expect(getAffinityEnforcement(false)).toBe('Flexible'); + }); +}); diff --git a/packages/manager/src/features/PlacementGroups/utils.ts b/packages/manager/src/features/PlacementGroups/utils.ts index b28d7a87938..09c23187eca 100644 --- a/packages/manager/src/features/PlacementGroups/utils.ts +++ b/packages/manager/src/features/PlacementGroups/utils.ts @@ -1,18 +1,28 @@ import { AFFINITY_TYPES } from '@linode/api-v4/lib/placement-groups'; import type { + AffinityEnforcement, CreatePlacementGroupPayload, PlacementGroup, Region, } from '@linode/api-v4'; +/** + * Helper to get the affinity enforcement readable string. + */ +export const getAffinityEnforcement = ( + affinityType: PlacementGroup['is_strict'] +): AffinityEnforcement => { + return affinityType ? 'Strict' : 'Flexible'; +}; + /** * Helper to get the number of Linodes in a Placement Group. */ export const getPlacementGroupLinodeCount = ( placementGroup: PlacementGroup ): number => { - return placementGroup.linode_ids.length; + return placementGroup.linodes.length; }; interface HasPlacementGroupReachedCapacityOptions { @@ -57,7 +67,7 @@ export const getLinodesFromAllPlacementGroups = ( } const linodeIds = allPlacementGroups.reduce((acc, placementGroup) => { - return [...acc, ...placementGroup.linode_ids]; + return [...acc, ...placementGroup.linodes.map((linode) => linode.linode)]; }, []); return Array.from(new Set(linodeIds)); diff --git a/packages/manager/src/hooks/usePlacementGroupsData.ts b/packages/manager/src/hooks/usePlacementGroupsData.ts index a7b43ea370f..00ee3c3d996 100644 --- a/packages/manager/src/hooks/usePlacementGroupsData.ts +++ b/packages/manager/src/hooks/usePlacementGroupsData.ts @@ -31,8 +31,8 @@ export const usePlacementGroupData = ({ const { data: linodes, error, isLoading } = useAllLinodesQuery( {}, { - '+or': placementGroup?.linode_ids.map((id) => ({ - id, + '+or': placementGroup?.linodes.map(({ linode }) => ({ + linode, })), } ); @@ -55,7 +55,7 @@ export const usePlacementGroupData = ({ const linodesCount = getPlacementGroupLinodeCount(placementGroup); const assignedLinodes = linodes?.filter((linode) => - placementGroup.linode_ids.includes(linode.id) + placementGroup.linodes.some((pgLinode) => pgLinode.linode === linode.id) ); const hasReachedCapacity = hasPlacementGroupReachedCapacity({ placementGroup, diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index ce213679d83..73f88217cf6 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -745,7 +745,7 @@ export const handlers = [ if (orFilters) { const filteredLinodes = linodes.filter((linode) => { const filteredById = orFilters.some( - (filter: { id: number }) => filter.id === linode.id + (filter: { linode: number }) => filter.linode === linode.id ); const filteredByRegion = orFilters.some( (filter: { region: string }) => filter.region === linode.region @@ -1077,7 +1077,6 @@ export const handlers = [ ) ); }), - rest.post('*object-storage/keys', (req, res, ctx) => { const { label, regions } = req.body as ObjectStorageKeyRequest; @@ -2111,9 +2110,47 @@ export const handlers = [ affinity_type: 'anti_affinity', id: Number(req.params.placementGroupId) ?? -1, label: 'pg-1', - linode_ids: [ - ...[0, 1, 2, 3, 5, 6, 7, 8, 43], - (req.body as any).linodes[0], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + { + is_compliant: true, + linode: 2, + }, + { + is_compliant: true, + linode: 3, + }, + { + is_compliant: true, + linode: 4, + }, + { + is_compliant: true, + linode: 5, + }, + { + is_compliant: true, + linode: 6, + }, + { + is_compliant: true, + linode: 7, + }, + { + is_compliant: true, + linode: 8, + }, + { + is_compliant: false, + linode: 43, + }, + { + is_compliant: true, + linode: (req.body as any).linodes[0], + }, ], }); @@ -2130,7 +2167,45 @@ export const handlers = [ affinity_type: 'anti_affinity', id: Number(req.params.placementGroupId) ?? -1, label: 'pg-1', - linode_ids: [0, 1, 2, 3, 5, 6, 7, 8, 43], + linodes: [ + { + is_compliant: true, + linode: 1, + }, + + { + is_compliant: true, + linode: 2, + }, + { + is_compliant: true, + linode: 3, + }, + { + is_compliant: true, + linode: 4, + }, + { + is_compliant: true, + linode: 5, + }, + { + is_compliant: true, + linode: 6, + }, + { + is_compliant: true, + linode: 7, + }, + { + is_compliant: true, + linode: 8, + }, + { + is_compliant: false, + linode: 43, + }, + ], }); return res(ctx.json(response)); diff --git a/packages/manager/src/queries/placementGroups.ts b/packages/manager/src/queries/placementGroups.ts index a1952724458..79e7dd39e2d 100644 --- a/packages/manager/src/queries/placementGroups.ts +++ b/packages/manager/src/queries/placementGroups.ts @@ -23,8 +23,8 @@ import type { AssignLinodesToPlacementGroupPayload, CreatePlacementGroupPayload, PlacementGroup, - RenamePlacementGroupPayload, UnassignLinodesFromPlacementGroupPayload, + UpdatePlacementGroupPayload, } from '@linode/api-v4'; export const queryKey = 'placement-groups'; @@ -84,7 +84,7 @@ export const useCreatePlacementGroup = () => { export const useMutatePlacementGroup = (id: number) => { const queryClient = useQueryClient(); - return useMutation({ + return useMutation({ mutationFn: (data) => renamePlacementGroup(id, data), onSuccess: (placementGroup) => { queryClient.invalidateQueries([queryKey, 'paginated']); diff --git a/packages/validation/src/linodes.schema.ts b/packages/validation/src/linodes.schema.ts index 89666f99af4..41497079e09 100644 --- a/packages/validation/src/linodes.schema.ts +++ b/packages/validation/src/linodes.schema.ts @@ -262,7 +262,6 @@ const MetadataSchema = object({ const PlacementGroupPayloadSchema = object({ id: number().notRequired().nullable(true), - compliant_only: boolean().notRequired().nullable(true), }); export const CreateLinodeSchema = object({