diff --git a/packages/manager/.changeset/pr-10787-upcoming-features-1723672538179.md b/packages/manager/.changeset/pr-10787-upcoming-features-1723672538179.md new file mode 100644 index 00000000000..f3a4d6841f4 --- /dev/null +++ b/packages/manager/.changeset/pr-10787-upcoming-features-1723672538179.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add 'Encrypt Volume' checkbox in Edit Volume drawer ([#10787](https://github.com/linode/manager/pull/10787)) diff --git a/packages/manager/src/components/Encryption/constants.tsx b/packages/manager/src/components/Encryption/constants.tsx index 2474412f56f..e484b2eafc1 100644 --- a/packages/manager/src/components/Encryption/constants.tsx +++ b/packages/manager/src/components/Encryption/constants.tsx @@ -95,3 +95,6 @@ export const BLOCK_STORAGE_ENCRYPTION_OVERHEAD_CAVEAT = export const BLOCK_STORAGE_USER_SIDE_ENCRYPTION_CAVEAT = 'User-side encryption on top of encryption-enabled volumes is discouraged at this time, as it could severely impact your volume performance.'; + +export const BLOCK_STORAGE_ENCRYPTION_SETTING_IMMUTABLE_COPY = + 'The encryption setting cannot be changed after creation.'; diff --git a/packages/manager/src/features/Volumes/EditVolumeDrawer.test.tsx b/packages/manager/src/features/Volumes/EditVolumeDrawer.test.tsx new file mode 100644 index 00000000000..a9c1597995d --- /dev/null +++ b/packages/manager/src/features/Volumes/EditVolumeDrawer.test.tsx @@ -0,0 +1,83 @@ +import { waitFor } from '@testing-library/react'; +import * as React from 'react'; + +import { accountFactory, volumeFactory } from 'src/factories'; +import { HttpResponse, http, server } from 'src/mocks/testServer'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { EditVolumeDrawer } from './EditVolumeDrawer'; + +const accountEndpoint = '*/v4/account'; +const encryptionLabelText = 'Encrypt Volume'; + +describe('EditVolumeDrawer', () => { + /* @TODO BSE: Remove feature flagging/conditionality once BSE is fully rolled out */ + + it('should display a disabled checkbox for volume encryption if the user has the account capability and the feature flag is on', async () => { + const volume = volumeFactory.build(); + + server.use( + http.get(accountEndpoint, () => { + return HttpResponse.json( + accountFactory.build({ capabilities: ['Block Storage Encryption'] }) + ); + }) + ); + + const { getByLabelText } = renderWithTheme( + , + { + flags: { blockStorageEncryption: true }, + } + ); + + await waitFor(() => { + expect(getByLabelText(encryptionLabelText)).not.toBeNull(); + expect(getByLabelText(encryptionLabelText)).toBeDisabled(); + }); + }); + + it('should not display a checkbox for volume encryption if the user has the account capability but the feature flag is off', async () => { + const volume = volumeFactory.build(); + + server.use( + http.get(accountEndpoint, () => { + return HttpResponse.json( + accountFactory.build({ capabilities: ['Block Storage Encryption'] }) + ); + }) + ); + + const { queryByRole } = renderWithTheme( + , + { + flags: { blockStorageEncryption: false }, + } + ); + + await waitFor(() => { + expect(queryByRole('checkbox')).not.toBeInTheDocument(); + }); + }); + + it('should not display a checkbox for volume encryption if the feature flag is on but the user lacks the account capability', async () => { + const volume = volumeFactory.build(); + + server.use( + http.get(accountEndpoint, () => { + return HttpResponse.json(accountFactory.build({ capabilities: [] })); + }) + ); + + const { queryByRole } = renderWithTheme( + , + { + flags: { blockStorageEncryption: true }, + } + ); + + await waitFor(() => { + expect(queryByRole('checkbox')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/packages/manager/src/features/Volumes/EditVolumeDrawer.tsx b/packages/manager/src/features/Volumes/EditVolumeDrawer.tsx index b4cf478c120..92108dd6406 100644 --- a/packages/manager/src/features/Volumes/EditVolumeDrawer.tsx +++ b/packages/manager/src/features/Volumes/EditVolumeDrawer.tsx @@ -1,10 +1,13 @@ -import { Volume } from '@linode/api-v4'; import { UpdateVolumeSchema } from '@linode/validation'; import { useFormik } from 'formik'; import React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; +import { Box } from 'src/components/Box'; +import { Checkbox } from 'src/components/Checkbox'; import { Drawer } from 'src/components/Drawer'; +import { BLOCK_STORAGE_ENCRYPTION_SETTING_IMMUTABLE_COPY } from 'src/components/Encryption/constants'; +import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { Notice } from 'src/components/Notice/Notice'; import { TagsInput } from 'src/components/TagsInput/TagsInput'; import { TextField } from 'src/components/TextField'; @@ -15,6 +18,8 @@ import { handleGeneralErrors, } from 'src/utilities/formikErrorUtils'; +import type { Volume } from '@linode/api-v4'; + interface Props { onClose: () => void; open: boolean; @@ -28,6 +33,10 @@ export const EditVolumeDrawer = (props: Props) => { const { mutateAsync: updateVolume } = useUpdateVolumeMutation(); + const { + isBlockStorageEncryptionFeatureEnabled, + } = useIsBlockStorageEncryptionFeatureEnabled(); + const isReadOnly = grants !== undefined && grants.volume.find((grant) => grant.id === volume?.id)?.permissions === @@ -114,6 +123,21 @@ export const EditVolumeDrawer = (props: Props) => { name="tags" value={values.tags?.map((t) => ({ label: t, value: t })) ?? []} /> + {isBlockStorageEncryptionFeatureEnabled && ( + + + + )}