Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Disable Cloning, Private IP, Backups for edge regions ([#10222](https://github.com/linode/manager/pull/10222))
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export const ActionMenu = React.memo((props: ActionMenuProps) => {
padding: '10px 10px 10px 16px',
}}
data-qa-action-menu-item={a.title}
data-testid={a.title}
disabled={a.disabled}
key={idx}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,20 @@ export const getIsLinodeCreateTypeEdgeSupported = (
typeof createType === 'undefined' // /linodes/create route
);
};

/**
* Util to determine whether a selected region is an edge region.
*
* @returns a boolean indicating whether or not the selected region is an edge region.
*/
export const getIsEdgeRegion = (
regionsData: Region[],
selectedRegion: string
) => {
return (
regionsData.find(
(region) =>
region.id === selectedRegion || region.label === selectedRegion
)?.site_type === 'edge'
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';

import { EntityDetail } from 'src/components/EntityDetail/EntityDetail';
import { Notice } from 'src/components/Notice/Notice';
import { getIsEdgeRegion } from 'src/components/RegionSelect/RegionSelect.utils';
import { getRestrictedResourceText } from 'src/features/Account/utils';
import { notificationContext as _notificationContext } from 'src/features/NotificationCenter/NotificationContext';
import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted';
Expand Down Expand Up @@ -81,6 +82,8 @@ export const LinodeEntityDetail = (props: Props) => {
const linodeRegionDisplay =
regions?.find((r) => r.id === linode.region)?.label ?? linode.region;

const linodeIsInEdgeRegion = getIsEdgeRegion(regions ?? [], linode.region);

let progress;
let transitionText;

Expand Down Expand Up @@ -113,6 +116,7 @@ export const LinodeEntityDetail = (props: Props) => {
ipv6={trimmedIPv6}
isVPCOnlyLinode={isVPCOnlyLinode}
linodeId={linode.id}
linodeIsInEdgeRegion={linodeIsInEdgeRegion}
linodeLabel={linode.label}
numCPUs={linode.specs.vcpus}
numVolumes={numberOfVolumes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface BodyProps {
ipv6: Linode['ipv6'];
isVPCOnlyLinode: boolean;
linodeId: number;
linodeIsInEdgeRegion: boolean;
linodeLabel: string;
numCPUs: number;
numVolumes: number;
Expand All @@ -65,6 +66,7 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
ipv6,
isVPCOnlyLinode,
linodeId,
linodeIsInEdgeRegion,
linodeLabel,
numCPUs,
numVolumes,
Expand Down Expand Up @@ -151,7 +153,9 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
{ heading: 'SSH Access', text: sshLink(ipv4[0]) },
{
heading: 'LISH Console via SSH',
text: lishLink(username, region, linodeLabel),
text: linodeIsInEdgeRegion
? 'N/A'
: lishLink(username, region, linodeLabel),
},
]}
gridSize={{ lg: 7, xs: 12 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,4 +324,27 @@ describe('AddonsPanel', () => {
).not.toBeInTheDocument();
});
});

it('should render a warning notice if isEdgeRegionSelected is true and disable backups and private ip checkbox', () => {
const propsWithEdgeRegionSelected = {
...props,
isEdgeRegionSelected: true,
};
const { getByTestId } = renderWithTheme(
<AddonsPanel {...propsWithEdgeRegionSelected} />
);
expect(getByTestId('notice-warning')).toBeInTheDocument();
expect(getByTestId('private_ip')).toHaveAttribute('aria-disabled', 'true');
expect(getByTestId('backups')).toHaveAttribute('aria-disabled', 'true');
});
it('should not render a warning notice if isEdgeRegionSelected is false', () => {
const propsWithEdgeRegionNotSelected = {
...props,
isEdgeRegionSelected: false,
};
const { queryByTestId } = renderWithTheme(
<AddonsPanel {...propsWithEdgeRegionNotSelected} />
);
expect(queryByTestId('notice-warning')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface AddonsPanelProps {
handleVLANChange: (updatedInterface: Interface) => void;
ipamAddress: string;
ipamError?: string;
isEdgeRegionSelected?: boolean;
isPrivateIPChecked: boolean;
labelError?: string;
linodesData?: Linode[];
Expand All @@ -60,6 +61,7 @@ export const AddonsPanel = React.memo((props: AddonsPanelProps) => {
handleVLANChange,
ipamAddress,
ipamError,
isEdgeRegionSelected,
isPrivateIPChecked,
labelError,
linodesData,
Expand Down Expand Up @@ -192,6 +194,12 @@ export const AddonsPanel = React.memo((props: AddonsPanelProps) => {
<TooltipIcon status="help" text={backupsDisabledReason} />
)}
</Typography>
{isEdgeRegionSelected && (
<Notice
text="Backups and Private IP are currently not available for Edge regions"
variant="warning"
/>
)}
{showBackupsWarning && (
<Notice variant="warning">
Linodes must have a disk formatted with an ext3 or ext4 file system
Expand All @@ -201,12 +209,19 @@ export const AddonsPanel = React.memo((props: AddonsPanelProps) => {
<StyledFormControlLabel
control={
<Checkbox
checked={
(accountBackups && !isEdgeRegionSelected) || props.backups
}
data-qa-check-backups={
accountBackups ? 'auto backup enabled' : 'auto backup disabled'
}
checked={accountBackups || props.backups}
disabled={
accountBackups ||
disabled ||
isBareMetal ||
isEdgeRegionSelected
}
data-testid="backups"
disabled={accountBackups || disabled || isBareMetal}
onChange={changeBackups}
/>
}
Expand All @@ -218,7 +233,7 @@ export const AddonsPanel = React.memo((props: AddonsPanelProps) => {
}
/>
<StyledTypography variant="body1">
{accountBackups ? (
{accountBackups && !isEdgeRegionSelected ? (
<React.Fragment>
You have enabled automatic backups for your account. This Linode
will automatically have backups enabled. To change this setting,{' '}
Expand All @@ -239,7 +254,7 @@ export const AddonsPanel = React.memo((props: AddonsPanelProps) => {
checked={isPrivateIPChecked}
data-qa-check-private-ip
data-testid="private_ip"
disabled={disabled}
disabled={disabled || isEdgeRegionSelected}
onChange={togglePrivateIP}
/>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { DocsLink } from 'src/components/DocsLink/DocsLink';
import { ErrorState } from 'src/components/ErrorState/ErrorState';
import { Link } from 'src/components/Link';
import { Notice } from 'src/components/Notice/Notice';
import { getIsEdgeRegion } from 'src/components/RegionSelect/RegionSelect.utils';
import { SelectRegionPanel } from 'src/components/SelectRegionPanel/SelectRegionPanel';
import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel';
import { TabLinkList } from 'src/components/Tabs/TabLinkList';
Expand Down Expand Up @@ -467,6 +468,11 @@ export class LinodeCreate extends React.PureComponent<
) &&
(imageIsCloudInitCompatible || linodeIsCloudInitCompatible);

const isEdgeRegionSelected = Boolean(
flags.gecko &&
getIsEdgeRegion(regionsData, this.props.selectedRegionID ?? '')
);

return (
<StyledForm>
<Grid className="py0">
Expand Down Expand Up @@ -732,6 +738,7 @@ export class LinodeCreate extends React.PureComponent<
handleVLANChange={this.props.handleVLANChange}
ipamAddress={this.props.ipamAddress || ''}
ipamError={hasErrorFor['interfaces[1].ipam_address']}
isEdgeRegionSelected={isEdgeRegionSelected}
isPrivateIPChecked={this.props.privateIPEnabled}
labelError={hasErrorFor['interfaces[1].label']}
linodesData={this.props.linodesData}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as React from 'react';
import VolumeIcon from 'src/assets/icons/entityIcons/volume.svg';
import { Paper } from 'src/components/Paper';
import { Placeholder } from 'src/components/Placeholder/Placeholder';
import { getIsEdgeRegion } from 'src/components/RegionSelect/RegionSelect.utils';
import { reportException } from 'src/exceptionReporting';
import { getAPIErrorFor } from 'src/utilities/getAPIErrorFor';

Expand Down Expand Up @@ -45,9 +46,6 @@ const errorResources = {
type: 'A plan selection',
};

const filterLinodesWithBackups = (linodes: Linode[]) =>
linodes.filter((linode) => linode.backups.enabled);

export class FromBackupsContent extends React.Component<CombinedProps, State> {
componentDidMount() {
this.mounted = true;
Expand All @@ -68,6 +66,7 @@ export class FromBackupsContent extends React.Component<CombinedProps, State> {
const {
errors,
linodesData,
regionsData,
selectedBackupID,
selectedLinodeID,
setBackupID,
Expand All @@ -80,6 +79,12 @@ export class FromBackupsContent extends React.Component<CombinedProps, State> {
(thisLinode) => thisLinode.backups.enabled
);

const filterLinodesWithBackups = (linodes: Linode[]) =>
linodes.filter(
(linode) =>
linode.backups.enabled && !getIsEdgeRegion(regionsData, linode.region) // Hide linodes that are in an edge region
);

return (
<StyledGrid>
{!userHasBackups ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Linode } from '@linode/api-v4/lib/linodes';
import * as React from 'react';
import { useHistory } from 'react-router-dom';

import VolumeIcon from 'src/assets/icons/entityIcons/volume.svg';
import { Paper } from 'src/components/Paper';
import { Placeholder } from 'src/components/Placeholder/Placeholder';
import { getIsEdgeRegion } from 'src/components/RegionSelect/RegionSelect.utils';
import { buildQueryStringForLinodeClone } from 'src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenuUtils';
import { useFlags } from 'src/hooks/useFlags';
import { extendType } from 'src/utilities/extendType';
Expand Down Expand Up @@ -73,6 +75,15 @@ export const FromLinodeContent = (props: CombinedProps) => {
}
};

const filterEdgeLinodes = (linodes: Linode[]) =>
linodes.filter(
(linode) => !getIsEdgeRegion(regionsData, linode.region) // Hide linodes that are in an edge region
);

const filteredLinodes = flags.gecko
? filterEdgeLinodes(linodesData)
: linodesData;

return (
// eslint-disable-next-line
<React.Fragment>
Expand Down Expand Up @@ -107,7 +118,7 @@ export const FromLinodeContent = (props: CombinedProps) => {
error={hasErrorFor('linode_id')}
handleSelection={handleSelectLinode}
header={'Select Linode to Clone From'}
linodes={linodesData}
linodes={filteredLinodes}
selectedLinodeID={selectedLinodeID}
showPowerActions
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';

import { BackupsPlaceholder } from './BackupsPlaceholder';

describe('BackupsPlaceholder', () => {
it('should disable the enable backups button if linodeIsInEdgeRegion is true', () => {
const { getByTestId } = renderWithTheme(
<BackupsPlaceholder
disabled={false}
linodeId={1}
linodeIsInEdgeRegion={true}
/>
);
expect(getByTestId('placeholder-button')).toHaveAttribute(
'aria-disabled',
'true'
);
});
it('should not disable the enable backups button if linodeIsInEdgeRegion is false', () => {
const { getByTestId } = renderWithTheme(
<BackupsPlaceholder
disabled={false}
linodeId={1}
linodeIsInEdgeRegion={false}
/>
);
expect(getByTestId('placeholder-button')).toHaveAttribute(
'aria-disabled',
'false'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ interface Props {
backupsMonthlyPrice?: PriceObject['monthly'];
disabled: boolean;
linodeId: number;
linodeIsInEdgeRegion?: boolean;
}

export const BackupsPlaceholder = React.memo((props: Props) => {
const { backupsMonthlyPrice, disabled, linodeId } = props;
const {
backupsMonthlyPrice,
disabled,
linodeId,
linodeIsInEdgeRegion,
} = props;

const [dialogOpen, setDialogOpen] = React.useState(false);

Expand Down Expand Up @@ -44,10 +50,14 @@ export const BackupsPlaceholder = React.memo((props: Props) => {
buttonProps={[
{
children: 'Enable Backups',
disabled,
disabled: disabled || linodeIsInEdgeRegion,
onClick: () => setDialogOpen(true),
tooltipText: linodeIsInEdgeRegion
? 'Backups are currently not available for Edge regions.'
: undefined,
},
]}
data-testid="backups"
icon={VolumeIcon}
isEntity
renderAsSecondary
Expand Down
Loading