Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upcoming: [M3-8139, M3-8140, M3-8141] – Add warning notices re: non-encryption when creating Images & enabling Backups #10521

Merged
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10521-tests-1717099871186.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tests
---

Add unit tests for CreateImageFromDiskDialog and EnableBackupsDialog and LDE-related E2E assertions for Create Image flow ([#10521](https://github.com/linode/manager/pull/10521))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add warning notices regarding non-encryption when creating Images and enabling Backups ([#10521](https://github.com/linode/manager/pull/10521))
176 changes: 175 additions & 1 deletion packages/manager/cypress/e2e/core/images/create-image.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,50 @@
import type { Linode } from '@linode/api-v4';
import type { Linode, Region } from '@linode/api-v4';
import { accountFactory, linodeFactory, regionFactory } from 'src/factories';
import { authenticate } from 'support/api/authentication';
import { mockGetAccount } from 'support/intercepts/account';
import {
mockAppendFeatureFlags,
mockGetFeatureFlagClientstream,
} from 'support/intercepts/feature-flags';
import { ui } from 'support/ui';
import { cleanUp } from 'support/util/cleanup';
import { makeFeatureFlagData } from 'support/util/feature-flags';
import { createTestLinode } from 'support/util/linodes';
import { randomLabel, randomPhrase } from 'support/util/random';
import { mockGetRegions } from 'support/intercepts/regions';
import {
mockGetLinodeDetails,
mockGetLinodes,
} from 'support/intercepts/linodes';

const mockRegions: Region[] = [
regionFactory.build({
capabilities: ['Linodes', 'Disk Encryption'],
id: 'us-east',
label: 'Newark, NJ',
site_type: 'core',
}),
regionFactory.build({
capabilities: ['Linodes', 'Disk Encryption'],
id: 'us-den-edge-1',
label: 'Edge - Denver, CO',
site_type: 'edge',
}),
];

const mockLinodes: Linode[] = [
linodeFactory.build({
label: 'core-region-linode',
region: mockRegions[0].id,
}),
linodeFactory.build({
label: 'edge-region-linode',
region: mockRegions[1].id,
}),
];

const DISK_ENCRYPTION_IMAGES_CAVEAT_COPY =
'Virtual Machine Images are not encrypted.';

authenticate();
describe('create image (e2e)', () => {
Expand Down Expand Up @@ -84,4 +125,137 @@ describe('create image (e2e)', () => {
});
});
});

it('displays notice informing user that Images are not encrypted, provided the LDE feature is enabled and the selected linode is not in an Edge region', () => {
// Mock feature flag -- @TODO LDE: Remove feature flag once LDE is fully rolled out
mockAppendFeatureFlags({
linodeDiskEncryption: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');

// Mock responses
const mockAccount = accountFactory.build({
capabilities: ['Linodes', 'Disk Encryption'],
});

mockGetAccount(mockAccount).as('getAccount');
mockGetRegions(mockRegions).as('getRegions');
mockGetLinodes(mockLinodes).as('getLinodes');

// intercept request
cy.visitWithLogin('/images/create');
cy.wait([
'@getFeatureFlags',
'@getClientStream',
'@getAccount',
'@getLinodes',
'@getRegions',
]);

// Find the Linode select and open it
cy.findByLabelText('Linode')
.should('be.visible')
.should('be.enabled')
.should('have.attr', 'placeholder', 'Select a Linode')
.click();

// Select the Linode
ui.autocompletePopper
.findByTitle(mockLinodes[0].label)
.should('be.visible')
.should('be.enabled')
.click();

// Check if notice is visible
cy.findByText(DISK_ENCRYPTION_IMAGES_CAVEAT_COPY).should('be.visible');
});

it('does not display a notice informing user that Images are not encrypted if the LDE feature is disabled', () => {
// Mock feature flag -- @TODO LDE: Remove feature flag once LDE is fully rolled out
mockAppendFeatureFlags({
linodeDiskEncryption: makeFeatureFlagData(false),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');

// Mock responses
const mockAccount = accountFactory.build({
capabilities: ['Linodes', 'Disk Encryption'],
});

mockGetAccount(mockAccount).as('getAccount');
mockGetRegions(mockRegions).as('getRegions');
mockGetLinodes(mockLinodes).as('getLinodes');

// intercept request
cy.visitWithLogin('/images/create');
cy.wait([
'@getFeatureFlags',
'@getClientStream',
'@getAccount',
'@getLinodes',
'@getRegions',
]);

// Find the Linode select and open it
cy.findByLabelText('Linode')
.should('be.visible')
.should('be.enabled')
.should('have.attr', 'placeholder', 'Select a Linode')
.click();

// Select the Linode
ui.autocompletePopper
.findByTitle(mockLinodes[0].label)
.should('be.visible')
.should('be.enabled')
.click();

// Check if notice is visible
cy.findByText(DISK_ENCRYPTION_IMAGES_CAVEAT_COPY).should('not.exist');
});

it('does not display a notice informing user that Images are not encrypted if the selected linode is in an Edge region', () => {
// Mock feature flag -- @TODO LDE: Remove feature flag once LDE is fully rolled out
mockAppendFeatureFlags({
linodeDiskEncryption: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');

// Mock responses
const mockAccount = accountFactory.build({
capabilities: ['Linodes', 'Disk Encryption'],
});

mockGetAccount(mockAccount).as('getAccount');
mockGetRegions(mockRegions).as('getRegions');
mockGetLinodes(mockLinodes).as('getLinodes');
mockGetLinodeDetails(mockLinodes[1].id, mockLinodes[1]);
dwiley-akamai marked this conversation as resolved.
Show resolved Hide resolved

// intercept request
cy.visitWithLogin('/images/create');
cy.wait([
'@getFeatureFlags',
'@getClientStream',
'@getAccount',
'@getRegions',
'@getLinodes',
]);

// Find the Linode select and open it
cy.findByLabelText('Linode')
.should('be.visible')
.should('be.enabled')
.should('have.attr', 'placeholder', 'Select a Linode')
.click();

// Select the Linode
ui.autocompletePopper
.findByTitle(mockLinodes[1].label)
.should('be.visible')
.should('be.enabled')
.click();

// Check if notice is visible
cy.findByText(DISK_ENCRYPTION_IMAGES_CAVEAT_COPY).should('not.exist');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ 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 DISK_ENCRYPTION_IMAGES_CAVEAT_COPY =
'Virtual Machine Images are not encrypted.';
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete';
import { Box } from 'src/components/Box';
import { Button } from 'src/components/Button/Button';
import { Checkbox } from 'src/components/Checkbox';
import { DISK_ENCRYPTION_IMAGES_CAVEAT_COPY } from 'src/components/DiskEncryption/constants';
import { useIsDiskEncryptionFeatureEnabled } from 'src/components/DiskEncryption/utils';
import { Link } from 'src/components/Link';
import { Notice } from 'src/components/Notice/Notice';
import { Paper } from 'src/components/Paper';
import { getIsEdgeRegion } from 'src/components/RegionSelect/RegionSelect.utils';
import { Stack } from 'src/components/Stack';
import { SupportLink } from 'src/components/SupportLink';
import { TagsInput } from 'src/components/TagsInput/TagsInput';
Expand All @@ -25,7 +28,9 @@ import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGran
import { useEventsPollingActions } from 'src/queries/events/events';
import { useCreateImageMutation } from 'src/queries/images';
import { useAllLinodeDisksQuery } from 'src/queries/linodes/disks';
import { useLinodeQuery } from 'src/queries/linodes/linodes';
import { useGrants } from 'src/queries/profile';
import { useRegionsQuery } from 'src/queries/regions/regions';

export const CreateImageTab = () => {
const [selectedLinodeId, setSelectedLinodeId] = React.useState<null | number>(
Expand Down Expand Up @@ -59,6 +64,10 @@ export const CreateImageTab = () => {
globalGrantType: 'add_images',
});

const {
isDiskEncryptionFeatureEnabled,
} = useIsDiskEncryptionFeatureEnabled();

const onSubmit = handleSubmit(async (values) => {
try {
await createImage(values);
Expand Down Expand Up @@ -92,6 +101,23 @@ export const CreateImageTab = () => {

const isRawDisk = selectedDisk?.filesystem === 'raw';

/*
We only want to display the notice about disk encryption if:
1. the Disk Encryption feature is enabled
2. the selected linode is not in an Edge region
*/
const { data: regionsData } = useRegionsQuery();

const { data: linode } = useLinodeQuery(
selectedLinodeId ?? -1,
Boolean(selectedLinodeId) && isDiskEncryptionFeatureEnabled
Copy link
Contributor Author

@dwiley-akamai dwiley-akamai May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the objective here is prevent this query from firing off constantly if a linode hasn't been selected yet or if LDE is not enabled

);

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

return (
<form onSubmit={onSubmit}>
<Stack spacing={2}>
Expand All @@ -113,7 +139,7 @@ export const CreateImageTab = () => {
<Typography variant="h2">Select Linode & Disk</Typography>
<Typography sx={{ maxWidth: { md: '80%', sm: '100%' } }}>
By default, Linode images are limited to 6144 MB of data per disk.
Ensure your content doesn't exceed this limit, or{' '}
Ensure your content doesn&rsquo;t exceed this limit, or{' '}
<SupportLink
entity={
selectedLinodeId !== null
Expand All @@ -123,9 +149,9 @@ export const CreateImageTab = () => {
text="open a support ticket"
title="Request to increase Image size limit when capturing from Linode disk"
/>{' '}
to request a higher limit. Additionally, images can't be created
from a raw disk or a disk that's formatted using a custom file
system.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prompted by linter -- small change to use apostrophe as we do in other places throughout the app instead of the single quotation mark

to request a higher limit. Additionally, images can&rsquo;t be
created from a raw disk or a disk that&rsquo;s formatted using a
custom file system.
</Typography>
<LinodeSelect
getOptionDisabled={
Expand Down Expand Up @@ -156,6 +182,17 @@ export const CreateImageTab = () => {
required
value={selectedLinodeId}
/>
{isDiskEncryptionFeatureEnabled &&
!linodeIsInEdgeRegion &&
selectedLinodeId !== null && (
<Notice variant="warning">
<Typography
sx={(theme) => ({ fontFamily: theme.font.normal })}
>
{DISK_ENCRYPTION_IMAGES_CAVEAT_COPY}
</Typography>
</Notice>
)}
<Controller
render={({ field, fieldState }) => (
<Autocomplete
Expand Down
Loading
Loading