diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index 4c02215ab43..cb1f120973b 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -14,7 +14,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Linked Node Pool firewall in Node Pool footer for LKE-E clusters ([#12779](https://github.com/linode/manager/pull/12779)) - IAM RBAC: Implement IAM RBAC permissions for NodeBalancer ([#12780](https://github.com/linode/manager/pull/12780)) - IAM RBAC: Implement IAM RBAC permissions for NodeBalancer summary tab ([#12790](https://github.com/linode/manager/pull/12790)) -- Additional device slots to Linode Config and Rescue Dialog to match new API limits ([#12791](https://github.com/linode/manager/pull/12791)) ### Changed: @@ -77,6 +76,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - ACLP-Metrics,Alerts: enforce validation for 100 characters for TextField components ([#12771](https://github.com/linode/manager/pull/12771)) - Disable legacy interface selection for Linode Interfaces when creating a Linode from backups ([#12772](https://github.com/linode/manager/pull/12772)) - UX feedback: Change /settings to /account-settings and profile/settings to profile/preferences ([#12785](https://github.com/linode/manager/pull/12785)) +- Add additional device slots to Linode Config and Rescue Dialog to match new API limits ([#12791](https://github.com/linode/manager/pull/12791)) - Add Firewall option to the Add Node Pool Drawer for LKE Enterprise Kubernetes Clusters ([#12793](https://github.com/linode/manager/pull/12793)) - CloudPulse-Metrics: Update CloudPulseRegionSelect.tsx to handle default linode region selection in firewalls contextual view ([#12805](https://github.com/linode/manager/pull/12805)) diff --git a/packages/manager/src/constants.ts b/packages/manager/src/constants.ts index 173fc717805..11bcf8ca075 100644 --- a/packages/manager/src/constants.ts +++ b/packages/manager/src/constants.ts @@ -290,3 +290,7 @@ export const DISALLOWED_IMAGE_REGIONS = [ // Default tooltip text for actions without permission export const NO_PERMISSION_TOOLTIP_TEXT = 'You do not have permission to perform this action.'; + +// Default device limit for Linode Configuration profiles. Configuration profiles can have have up to 64 +// devices depending on the Linode's RAM, but will always be able to have at least 8. +export const DEFAULT_DEVICE_LIMIT = 8; diff --git a/packages/manager/src/dev-tools/FeatureFlagTool.tsx b/packages/manager/src/dev-tools/FeatureFlagTool.tsx index 4a2d90d9755..82fa100bb9c 100644 --- a/packages/manager/src/dev-tools/FeatureFlagTool.tsx +++ b/packages/manager/src/dev-tools/FeatureFlagTool.tsx @@ -26,6 +26,7 @@ const options: { flag: keyof Flags; label: string }[] = [ { flag: 'apl', label: 'Akamai App Platform' }, { flag: 'aplGeneralAvailability', label: 'Akamai App Platform GA' }, { flag: 'blockStorageEncryption', label: 'Block Storage Encryption (BSE)' }, + { flag: 'blockStorageVolumeLimit', label: 'Block Storage Volume Limit' }, { flag: 'cloudNat', label: 'Cloud NAT' }, { flag: 'disableLargestGbPlans', label: 'Disable Largest GB Plans' }, { flag: 'gecko2', label: 'Gecko' }, diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 9bcab347af9..bcb6d0d6bf7 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -144,6 +144,7 @@ export interface Flags { apl: boolean; aplGeneralAvailability: boolean; blockStorageEncryption: boolean; + blockStorageVolumeLimit: boolean; cloudManagerDesignUpdatesBanner: DesignUpdatesBannerFlag; cloudNat: CloudNatFlag; databaseAdvancedConfig: boolean; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx index b7b1982cd61..2fdd949f2cf 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx @@ -68,7 +68,7 @@ import { StyledFormGroup, StyledRadioGroup, } from './LinodeConfigDialog.styles'; -import { getPrimaryInterfaceIndex } from './utilities'; +import { getPrimaryInterfaceIndex, useGetDeviceLimit } from './utilities'; import type { ExtendedInterface } from '../LinodeSettings/InterfaceSelect'; import type { @@ -243,7 +243,7 @@ export const LinodeConfigDialog = (props: Props) => { // eslint-disable-next-line no-console console.warn('Invalid memory value:', availableMemory); } - const deviceLimit = Math.max(8, Math.min(availableMemory / 1024, 64)); + const deviceLimit = useGetDeviceLimit(availableMemory); const { isLinodeInterfacesEnabled } = useIsLinodeInterfacesEnabled(); diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.test.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.test.ts index b5d4ed998ee..c5b9a3b8ee4 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.test.ts +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.test.ts @@ -1,6 +1,9 @@ import { linodeConfigInterfaceFactory } from '@linode/utilities'; +import { renderHook } from '@testing-library/react'; -import { getPrimaryInterfaceIndex } from './utilities'; +import { wrapWithTheme } from 'src/utilities/testHelpers'; + +import { getPrimaryInterfaceIndex, useGetDeviceLimit } from './utilities'; describe('getPrimaryInterfaceIndex', () => { it('returns null if there are no interfaces', () => { @@ -34,3 +37,36 @@ describe('getPrimaryInterfaceIndex', () => { expect(getPrimaryInterfaceIndex(interfaces)).toBe(null); }); }); + +describe('useGetDeviceLimit', () => { + it.each([131072, 65536, 16384, 1024])( + 'should always return 8 as the device limit', + (value) => { + const { result } = renderHook(() => useGetDeviceLimit(value), { + wrapper: (ui) => + wrapWithTheme(ui.children, { + flags: { blockStorageVolumeLimit: false }, + }), + }); + expect(result.current).toEqual(8); + } + ); + + it.each([ + [131072, 64], + [65536, 64], + [16384, 16], + [1024, 8], + ])( + 'should calculate the correct device limit for %d ram to be %d', + (value, expected) => { + const { result } = renderHook(() => useGetDeviceLimit(value), { + wrapper: (ui) => + wrapWithTheme(ui.children, { + flags: { blockStorageVolumeLimit: true }, + }), + }); + expect(result.current).toEqual(expected); + } + ); +}); diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.ts index 2b12640e578..8c2db6b8acf 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.ts +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/utilities.ts @@ -1,5 +1,8 @@ import { isEmpty } from '@linode/api-v4'; +import { DEFAULT_DEVICE_LIMIT } from 'src/constants'; +import { useFlags } from 'src/hooks/useFlags'; + import type { Interface } from '@linode/api-v4'; /** @@ -47,3 +50,20 @@ export const getPrimaryInterfaceIndex = (interfaces: Interface[]) => { // As an example, this is the case when a Linode only has a VLAN interface. return null; }; + +/** + * Determines the maximum available Linodes allowed for a configuration profile + * + * returns MAX(8, MIN(ram / 1024, 64)) + * + * @param ram the Linode's available ram + * @returns the device limit allowed + */ +export const useGetDeviceLimit = (ram: number) => { + const flags = useFlags(); + if (flags.blockStorageVolumeLimit) { + return Math.max(DEFAULT_DEVICE_LIMIT, Math.min(ram / 1024, 64)); + } + + return DEFAULT_DEVICE_LIMIT; +}; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx index fc987d9f16e..622be8630c3 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx @@ -23,6 +23,7 @@ import * as React from 'react'; import { useEventsPollingActions } from 'src/queries/events/events'; import { deviceSlots } from '../LinodeConfigs/constants'; +import { useGetDeviceLimit } from '../LinodeConfigs/utilities'; import { LinodePermissionsError } from '../LinodePermissionsError'; import { DeviceSelection } from './DeviceSelection'; import { RescueDescription } from './RescueDescription'; @@ -79,7 +80,7 @@ export const StandardRescueDialog = (props: Props) => { // eslint-disable-next-line no-console console.warn('Invalid memory value:', availableMemory); } - const overallDeviceLimit = Math.max(8, Math.min(availableMemory / 1024, 64)); + const overallDeviceLimit = useGetDeviceLimit(availableMemory); const rescueDeviceLimit = overallDeviceLimit - 2; const {