Skip to content

Commit c9a55b6

Browse files
authored
upcoming: [M3-10532] - Disable legacy interface selection in Linode Interface UI when creating Linode from backups (linode#12772)
* disable legacy interfaces when creat ing from backup * tooltip for vpc legacy flow * minor updates * something like this? * changeset * whoops didn't save + add test * tooltip when disabled * update tooltip * no selection when disabled * add test case * this feels really weird ngl * address feedback * this seems more legit * remove stray console
1 parent a67c9ac commit c9a55b6

File tree

7 files changed

+101
-11
lines changed

7 files changed

+101
-11
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Disable legacy interface selection for Linode Interfaces when creating a Linode from backups ([#12772](https://github.com/linode/manager/pull/12772))

packages/manager/src/features/Linodes/LinodeCreate/Networking/InterfaceType.tsx

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { firewallQueries, useQueryClient } from '@linode/queries';
22
import {
3+
Box,
34
FormControl,
45
Radio,
56
RadioGroup,
@@ -9,11 +10,12 @@ import {
910
import { Grid } from '@mui/material';
1011
import { useSnackbar } from 'notistack';
1112
import React from 'react';
12-
import { useController, useFormContext } from 'react-hook-form';
13+
import { useController, useFormContext, useWatch } from 'react-hook-form';
1314

1415
import { FormLabel } from 'src/components/FormLabel';
1516
import { SelectionCard } from 'src/components/SelectionCard/SelectionCard';
1617

18+
import { useGetLinodeCreateType } from '../Tabs/utils/useGetLinodeCreateType';
1719
import { getDefaultFirewallForInterfacePurpose } from './utilities';
1820

1921
import type { LinodeCreateFormValues } from '../utilities';
@@ -57,6 +59,16 @@ export const InterfaceType = ({ index }: Props) => {
5759
name: `linodeInterfaces.${index}.purpose`,
5860
});
5961

62+
const interfaceGeneration = useWatch({
63+
control,
64+
name: 'interface_generation',
65+
});
66+
67+
const createType = useGetLinodeCreateType();
68+
const isCreatingFromBackup = createType === 'Backups';
69+
70+
const disabled = isCreatingFromBackup && interfaceGeneration !== 'linode';
71+
6072
const onChange = async (value: InterfacePurpose) => {
6173
// Change the interface purpose (Public, VPC, VLAN)
6274
field.onChange(value);
@@ -96,7 +108,20 @@ export const InterfaceType = ({ index }: Props) => {
96108

97109
return (
98110
<FormControl>
99-
<FormLabel id="network-connection-label">Network Connection</FormLabel>
111+
<Box alignItems="center" display="flex" flexDirection="row">
112+
<FormLabel id="network-connection-label">Network Connection</FormLabel>
113+
{disabled && (
114+
<TooltipIcon
115+
status="info"
116+
sxTooltipIcon={{
117+
padding: 0,
118+
marginLeft: '8px',
119+
marginBottom: '8px',
120+
}}
121+
text="You cannot use Configuration Profile Interfaces when deploying to a new Linode from a backup."
122+
/>
123+
)}
124+
</Box>
100125
<Typography id="network-connection-helper-text">
101126
The default interface used by this Linode to route network traffic.
102127
Additional interfaces can be added after the Linode is created.
@@ -109,7 +134,8 @@ export const InterfaceType = ({ index }: Props) => {
109134
<Grid container spacing={2}>
110135
{interfaceTypes.map((interfaceType) => (
111136
<SelectionCard
112-
checked={field.value === interfaceType.purpose}
137+
checked={disabled ? false : field.value === interfaceType.purpose}
138+
disabled={disabled}
113139
gridSize={{
114140
md: 3,
115141
sm: 12,
@@ -119,7 +145,12 @@ export const InterfaceType = ({ index }: Props) => {
119145
key={interfaceType.purpose}
120146
onClick={() => onChange(interfaceType.purpose)}
121147
renderIcon={() => (
122-
<Radio checked={field.value === interfaceType.purpose} />
148+
<Radio
149+
checked={
150+
disabled ? false : field.value === interfaceType.purpose
151+
}
152+
disabled={disabled}
153+
/>
123154
)}
124155
renderVariant={() => (
125156
<TooltipIcon

packages/manager/src/features/Linodes/LinodeCreate/Tabs/utils/useGetLinodeCreateType.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export const linodesCreateTypes = Array.from(linodesCreateTypesMap.keys());
2828
export const useGetLinodeCreateType = () => {
2929
const { pathname } = useLocation() as { pathname: LinkProps['to'] };
3030

31+
return getLinodeCreateType(pathname);
32+
};
33+
34+
export const getLinodeCreateType = (pathname: LinkProps['to']) => {
3135
switch (pathname) {
3236
case '/linodes/create/backups':
3337
return 'Backups';

packages/manager/src/features/Linodes/LinodeCreate/VPC/VPC.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ export const VPC = () => {
7171
: 'Assign this Linode to an existing VPC.';
7272

7373
const createType = useGetLinodeCreateType();
74+
const isCreatingFromBackup = createType === 'Backups';
75+
const disabled = !regionSupportsVPCs || isCreatingFromBackup;
7476

7577
const vpcFormEventOptions: LinodeCreateFormEventOptions = {
7678
createType: createType ?? 'OS',
@@ -82,7 +84,16 @@ export const VPC = () => {
8284
return (
8385
<Paper data-testid="vpc-panel">
8486
<Stack spacing={2}>
85-
<Typography variant="h2">VPC</Typography>
87+
<Box alignItems="center" display="flex" flexDirection="row">
88+
<Typography variant="h2">VPC</Typography>
89+
{isCreatingFromBackup && (
90+
<TooltipIcon
91+
status="info"
92+
sxTooltipIcon={{ p: 0, marginLeft: '8px' }}
93+
text="You cannot assign a VPC when deploying to a new Linode from a backup."
94+
/>
95+
)}
96+
</Box>
8697
<Typography>
8798
{copy}{' '}
8899
<Link
@@ -104,7 +115,7 @@ export const VPC = () => {
104115
name="interfaces.0.vpc_id"
105116
render={({ field, fieldState }) => (
106117
<Autocomplete
107-
disabled={!regionSupportsVPCs}
118+
disabled={disabled}
108119
errorText={error?.[0].reason ?? fieldState.error?.message}
109120
helperText={
110121
!regionId

packages/manager/src/features/Linodes/LinodeCreate/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ import {
2929
useVMHostMaintenanceEnabled,
3030
} from 'src/features/Account/utils';
3131
import { usePermissions } from 'src/features/IAM/hooks/usePermissions';
32-
import { useGetLinodeCreateType } from 'src/features/Linodes/LinodeCreate/Tabs/utils/useGetLinodeCreateType';
32+
import {
33+
getLinodeCreateType,
34+
useGetLinodeCreateType,
35+
} from 'src/features/Linodes/LinodeCreate/Tabs/utils/useGetLinodeCreateType';
3336
import { useFlags } from 'src/hooks/useFlags';
3437
import { useSecureVMNoticesEnabled } from 'src/hooks/useSecureVMNoticesEnabled';
3538
import { useTabs } from 'src/hooks/useTabs';
@@ -146,8 +149,10 @@ export const LinodeCreate = () => {
146149
handleTabChange(index);
147150

148151
if (index !== tabIndex) {
152+
const newTab = tabs[index];
153+
const newLinodeCreateType = getLinodeCreateType(newTab.to);
149154
// Get the default values for the new tab and reset the form
150-
defaultValues(linodeCreateType, search, queryClient, {
155+
defaultValues(newLinodeCreateType, search, queryClient, {
151156
isLinodeInterfacesEnabled,
152157
isVMHostMaintenanceEnabled,
153158
}).then(form.reset);

packages/manager/src/features/Linodes/LinodeCreate/utilities.test.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,38 @@ describe('getLinodeCreatePayload', () => {
7070
placement_group: undefined,
7171
});
7272
});
73+
74+
it('should remove interface from the payload if using legacy interfaces with the new UI and the linode is being created from backups', () => {
75+
const values = {
76+
...createLinodeRequestFactory.build({
77+
interface_generation: 'legacy_config',
78+
backup_id: 1,
79+
}),
80+
linodeInterfaces: [{ purpose: 'public', public: {} }],
81+
} as LinodeCreateFormValues;
82+
83+
expect(
84+
getLinodeCreatePayload(values, {
85+
isShowingNewNetworkingUI: true,
86+
}).interfaces
87+
).toEqual(undefined);
88+
});
89+
90+
it('should not remove interface from the payload when using new interfaces and creating from a backup', () => {
91+
const values = {
92+
...createLinodeRequestFactory.build({
93+
interface_generation: 'linode',
94+
backup_id: 1,
95+
}),
96+
linodeInterfaces: [{ purpose: 'public', public: {} }],
97+
} as LinodeCreateFormValues;
98+
99+
expect(
100+
getLinodeCreatePayload(values, {
101+
isShowingNewNetworkingUI: true,
102+
}).interfaces
103+
).toEqual([{ public: {}, vpc: null, vlan: null }]);
104+
});
73105
});
74106

75107
describe('getInterfacesPayload', () => {

packages/manager/src/features/Linodes/LinodeCreate/utilities.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@ export const getLinodeCreatePayload = (
9898
);
9999
values.firewall_id = undefined;
100100
} else {
101-
values.interfaces = formValues.linodeInterfaces.map(
102-
getLegacyInterfaceFromLinodeInterface
103-
);
101+
values.interfaces = formValues.backup_id
102+
? undefined
103+
: formValues.linodeInterfaces.map(
104+
getLegacyInterfaceFromLinodeInterface
105+
);
104106
}
105107
} else {
106108
values.interfaces = getInterfacesPayload(

0 commit comments

Comments
 (0)