Skip to content

Commit

Permalink
upcoming: [M3-8019] – Add Encrypted/Not Encrypted status to Linode De…
Browse files Browse the repository at this point in the history
…tail summary header (#10537)
  • Loading branch information
dwiley-akamai authored Jun 4, 2024
1 parent bbbca9b commit 9e6474a
Show file tree
Hide file tree
Showing 15 changed files with 142 additions and 7 deletions.
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-10537-changed-1717182754934.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

Add lke_cluster_id to Linode interface ([#10537](https://github.com/linode/manager/pull/10537))
1 change: 1 addition & 0 deletions packages/api-v4/src/linodes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Linode {
ipv4: string[];
ipv6: string | null;
label: string;
lke_cluster_id: number | null;
placement_group?: PlacementGroupPayload; // If not in a placement group, this will be excluded from the response.
type: string | null;
status: LinodeStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add Encrypted / Not Encrypted status to Linode Detail header ([#10537](https://github.com/linode/manager/pull/10537))
4 changes: 4 additions & 0 deletions packages/manager/src/__data__/linodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down Expand Up @@ -69,6 +70,7 @@ export const linode2: Linode = {
ipv4: ['97.107.143.49'],
ipv6: '2600:3c03::f03c:91ff:fe0a:0d7a/64',
label: 'another-test',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down Expand Up @@ -114,6 +116,7 @@ export const linode3: Linode = {
ipv4: ['97.107.143.49'],
ipv6: '2600:3c03::f03c:91ff:fe0a:0d7a/64',
label: 'another-test',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down Expand Up @@ -159,6 +162,7 @@ export const linode4: Linode = {
ipv4: ['97.107.143.49'],
ipv6: '2600:3c03::f03c:91ff:fe0a:0d7a/64',
label: 'another-test-eu',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down
1 change: 1 addition & 0 deletions packages/manager/src/assets/icons/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/manager/src/assets/icons/unlock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/manager/src/components/DiskEncryption/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ export const DISK_ENCRYPTION_BACKUPS_CAVEAT_COPY =
export const DISK_ENCRYPTION_NODE_POOL_GUIDANCE_COPY =
'To enable disk encryption, delete the node pool and create a new node pool. New node pools are always encrypted.';

export const UNENCRYPTED_STANDARD_LINODE_GUIDANCE_COPY =
'Rebuild this Linode to enable or disable disk encryption.';

export const DISK_ENCRYPTION_IMAGES_CAVEAT_COPY =
'Virtual Machine Images are not encrypted.';
1 change: 1 addition & 0 deletions packages/manager/src/factories/linodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export const linodeFactory = Factory.Sync.makeFactory<Linode>({
ipv4: ['50.116.6.212', '192.168.203.1'],
ipv6: '2600:3c00::f03c:92ff:fee2:6c40/64',
label: Factory.each((i) => `linode-${i}`),
lke_cluster_id: null,
placement_group: placementGroupFactory.build({
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,10 @@ export const EncryptedStatus = ({
</>
) : encryptionStatus === 'disabled' ? (
<>
<Unlock />
<StyledTypography>Not Encrypted</StyledTypography>
<Unlock style={{ minWidth: 16 }} />
<StyledTypography sx={{ whiteSpace: 'nowrap' }}>
Not Encrypted
</StyledTypography>
{tooltipText ? <TooltipIcon status="help" text={tooltipText} /> : null}
</>
) : null;
Expand Down
59 changes: 59 additions & 0 deletions packages/manager/src/features/Linodes/LinodeEntityDetail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { HttpResponse, http, server } from 'src/mocks/testServer';
import { queryClientFactory } from 'src/queries/base';
import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers';

import { encryptionStatusTestId } from '../Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable';
import { LinodeEntityDetail } from './LinodeEntityDetail';
import { getSubnetsString } from './LinodeEntityDetailBody';
import { LinodeHandlers } from './LinodesLanding/LinodesLanding';
Expand All @@ -34,6 +35,29 @@ describe('Linode Entity Detail', () => {
const vpcSectionTestId = 'vpc-section-title';
const assignedVPCLabelTestId = 'assigned-vpc-label';

const mocks = vi.hoisted(() => {
return {
useIsDiskEncryptionFeatureEnabled: vi.fn(),
};
});

vi.mock('src/components/DiskEncryption/utils.ts', async () => {
const actual = await vi.importActual<any>(
'src/components/DiskEncryption/utils.ts'
);
return {
...actual,
__esModule: true,
useIsDiskEncryptionFeatureEnabled: mocks.useIsDiskEncryptionFeatureEnabled.mockImplementation(
() => {
return {
isDiskEncryptionFeatureEnabled: false, // indicates the feature flag is off or account capability is absent
};
}
),
};
});

it('should not display the VPC section if the linode is not assigned to a VPC', async () => {
const account = accountFactory.build({
capabilities: [...accountCapabilitiesWithoutVPC, 'VPCs'],
Expand Down Expand Up @@ -104,6 +128,41 @@ describe('Linode Entity Detail', () => {
expect(getByTestId(assignedVPCLabelTestId).innerHTML).toEqual('test-vpc');
});
});

it('should not display the encryption status of the linode if the account lacks the capability or the feature flag is off', () => {
// situation where isDiskEncryptionFeatureEnabled === false
const { queryByTestId } = renderWithTheme(
<LinodeEntityDetail
handlers={handlers}
id={10}
linode={linode}
openTagDrawer={vi.fn()}
/>
);
const encryptionStatusFragment = queryByTestId(encryptionStatusTestId);

expect(encryptionStatusFragment).not.toBeInTheDocument();
});

it('should display the encryption status of the linode when Disk Encryption is enabled and the user has the account capability', () => {
mocks.useIsDiskEncryptionFeatureEnabled.mockImplementationOnce(() => {
return {
isDiskEncryptionFeatureEnabled: true,
};
});

const { queryByTestId } = renderWithTheme(
<LinodeEntityDetail
handlers={handlers}
id={10}
linode={linode}
openTagDrawer={vi.fn()}
/>
);
const encryptionStatusFragment = queryByTestId(encryptionStatusTestId);

expect(encryptionStatusFragment).toBeInTheDocument();
});
});

describe('getSubnetsString function', () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/manager/src/features/Linodes/LinodeEntityDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@ export const LinodeEntityDetail = (props: Props) => {
body={
<LinodeEntityDetailBody
configInterfaceWithVPC={configInterfaceWithVPC}
encryptionStatus={linode.disk_encryption}
gbRAM={linode.specs.memory / 1024}
gbStorage={linode.specs.disk / 1024}
ipv4={linode.ipv4}
ipv6={trimmedIPv6}
isLKELinode={Boolean(linode.lke_cluster_id)}
isVPCOnlyLinode={isVPCOnlyLinode}
linodeId={linode.id}
linodeIsInDistributedRegion={linodeIsInDistributedRegion}
Expand Down
54 changes: 50 additions & 4 deletions packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import Grid from '@mui/material/Unstable_Grid2';
import * as React from 'react';
import { HashLink } from 'react-router-hash-link';

import { Box } from 'src/components/Box';
import {
DISK_ENCRYPTION_NODE_POOL_GUIDANCE_COPY as UNENCRYPTED_LKE_LINODE_GUIDANCE_COPY,
UNENCRYPTED_STANDARD_LINODE_GUIDANCE_COPY,
} from 'src/components/DiskEncryption/constants';
import { useIsDiskEncryptionFeatureEnabled } from 'src/components/DiskEncryption/utils';
import { Link } from 'src/components/Link';
import { Typography, TypographyProps } from 'src/components/Typography';
import { AccessTable } from 'src/features/Linodes/AccessTable';
import { useProfile } from 'src/queries/profile';
import { pluralize } from 'src/utilities/pluralize';

import { encryptionStatusTestId } from '../Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable';
import { EncryptedStatus } from '../Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable';
import {
StyledBodyGrid,
StyledColumnLabelGrid,
Expand All @@ -24,7 +32,11 @@ import { ipv4TableID } from './LinodesDetail/LinodeNetworking/LinodeIPAddresses'
import { lishLink, sshLink } from './LinodesDetail/utilities';
import { LinodeHandlers } from './LinodesLanding/LinodesLanding';

import type { Interface, Linode } from '@linode/api-v4/lib/linodes/types';
import type {
EncryptionStatus,
Interface,
Linode,
} from '@linode/api-v4/lib/linodes/types';
import type { Subnet } from '@linode/api-v4/lib/vpcs';

interface LinodeEntityDetailProps {
Expand All @@ -41,10 +53,12 @@ export interface Props extends LinodeEntityDetailProps {

export interface BodyProps {
configInterfaceWithVPC?: Interface;
encryptionStatus: EncryptionStatus | undefined;
gbRAM: number;
gbStorage: number;
ipv4: Linode['ipv4'];
ipv6: Linode['ipv6'];
isLKELinode: boolean; // indicates whether linode belongs to an LKE cluster
isVPCOnlyLinode: boolean;
linodeId: number;
linodeIsInDistributedRegion: boolean;
Expand All @@ -58,10 +72,12 @@ export interface BodyProps {
export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
const {
configInterfaceWithVPC,
encryptionStatus,
gbRAM,
gbStorage,
ipv4,
ipv6,
isLKELinode,
isVPCOnlyLinode,
linodeId,
linodeIsInDistributedRegion,
Expand All @@ -77,6 +93,14 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {

const theme = useTheme();

const {
isDiskEncryptionFeatureEnabled,
} = useIsDiskEncryptionFeatureEnabled();

// @ TODO LDE: Remove usages of this variable once LDE is fully rolled out (being used to determine formatting adjustments currently)
const isDisplayingEncryptedStatus =
isDiskEncryptionFeatureEnabled && Boolean(encryptionStatus);

// Filter and retrieve subnets associated with a specific Linode ID
const linodeAssociatedSubnets = vpcLinodeIsAssignedTo?.subnets.filter(
(subnet) => subnet.linodes.some((linode) => linode.id === linodeId)
Expand All @@ -97,11 +121,14 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
<Grid
container
flexDirection={matchesLgUp ? 'row' : 'column'}
sm={3}
sm={isDisplayingEncryptedStatus ? 4 : 3}
spacing={0}
xs={12}
>
<StyledColumnLabelGrid mb={matchesLgUp ? 0 : 2} xs={12}>
<StyledColumnLabelGrid
mb={matchesLgUp && !isDisplayingEncryptedStatus ? 0 : 2}
xs={12}
>
Summary
</StyledColumnLabelGrid>
<StyledSummaryGrid container spacing={1}>
Expand All @@ -121,9 +148,28 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
{pluralize('Volume', 'Volumes', numVolumes)}
</Typography>
</Grid>
{isDiskEncryptionFeatureEnabled && encryptionStatus && (
<Grid>
<Box
alignItems="center"
data-testid={encryptionStatusTestId}
display="flex"
flexDirection="row"
>
<EncryptedStatus
tooltipText={
isLKELinode
? UNENCRYPTED_LKE_LINODE_GUIDANCE_COPY
: UNENCRYPTED_STANDARD_LINODE_GUIDANCE_COPY
}
encryptionStatus={encryptionStatus}
/>
</Box>
</Grid>
)}
</StyledSummaryGrid>
</Grid>
<Grid container sm={9} xs={12}>
<Grid container sm={isDisplayingEncryptedStatus ? 8 : 9} xs={12}>
<Grid container xs={12}>
<AccessTable
footer={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const props: AddonsPanelProps = {
ipv4: ['45.56.75.98'],
ipv6: '2600:3c00::f03c:93ff:fe85:576d/128',
label: 'test_instance',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down Expand Up @@ -107,6 +108,7 @@ const props: AddonsPanelProps = {
ipv4: ['192.168.139.183', '139.144.17.202'],
ipv6: '2600:3c04::f03c:93ff:fe75:0612/128',
label: 'debian-ca-central',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down Expand Up @@ -151,6 +153,7 @@ const props: AddonsPanelProps = {
ipv4: ['45.79.74.95'],
ipv6: '2600:3c01::f03c:93ff:fe75:e4f9/128',
label: 'almalinux-us-west',
lke_cluster_id: null,
placement_group: {
affinity_type: 'anti_affinity:local',
id: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe('LinodeRow', () => {
ipv6={linode.ipv6 || ''}
key={`linode-row-${1}`}
label={linode.label}
lke_cluster_id={linode.lke_cluster_id}
placement_group={linode.placement_group}
region={linode.region}
specs={linode.specs}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export const ListView = (props: RenderLinodesProps) => {
ipv6={linode.ipv6 || ''}
key={`linode-row-${idx}`}
label={linode.label}
lke_cluster_id={linode.lke_cluster_id}
maintenance={linode.maintenance}
placement_group={linode.placement_group}
region={linode.region}
specs={linode.specs}
Expand All @@ -54,7 +56,6 @@ export const ListView = (props: RenderLinodesProps) => {
type={linode.type}
updated={linode.updated}
watchdog_enabled={linode.watchdog_enabled}
maintenance={linode.maintenance}
/>
))}
</>
Expand Down

0 comments on commit 9e6474a

Please sign in to comment.