diff --git a/packages/manager/.changeset/pr-10868-upcoming-features-1725546685590.md b/packages/manager/.changeset/pr-10868-upcoming-features-1725546685590.md new file mode 100644 index 00000000000..8677ed9acc1 --- /dev/null +++ b/packages/manager/.changeset/pr-10868-upcoming-features-1725546685590.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add conditional client library update required reboot notice to Volume Create page ([#10868](https://github.com/linode/manager/pull/10868)) diff --git a/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts index 6dfd3bc07dd..654bfa0ddb2 100644 --- a/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts @@ -1,6 +1,9 @@ import type { Linode, Region } from '@linode/api-v4'; import { createTestLinode } from 'support/util/linodes'; -import { createLinodeRequestFactory } from 'src/factories/linodes'; +import { + createLinodeRequestFactory, + linodeFactory, +} from 'src/factories/linodes'; import { authenticate } from 'support/api/authentication'; import { cleanUp } from 'support/util/cleanup'; import { @@ -15,6 +18,10 @@ import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags'; import { accountFactory, regionFactory, volumeFactory } from 'src/factories'; import { mockGetAccount } from 'support/intercepts/account'; import { mockGetRegions } from 'support/intercepts/regions'; +import { + mockGetLinodeDetails, + mockGetLinodes, +} from 'support/intercepts/linodes'; // Local storage override to force volume table to list up to 100 items. // This is a workaround while we wait to get stuck volumes removed. @@ -132,6 +139,10 @@ describe('volume create flow', () => { .should('be.visible') .click(); + // @TODO BSE: once BSE is fully rolled out, check for the notice (selected linode doesn't have + // blockstorage_encryption capability + user checked "Encrypt Volume" checkbox) instead of the absence of it + cy.findByText(CLIENT_LIBRARY_UPDATE_COPY).should('not.exist'); + cy.findByText('Create Volume').click(); cy.wait('@createVolume'); @@ -162,7 +173,112 @@ describe('volume create flow', () => { }); /* - * - Checks for Block Storage Encryption notices in the Create/Attach Volume drawer from the + * - Checks for Block Storage Encryption client library update notice on the Volume Create page. + */ + it('displays a warning notice on Volume Create page re: rebooting for client library updates under the appropriate conditions', () => { + // Conditions: Block Storage encryption feature flag is on; user has Block Storage Encryption capability; volume being created is encrypted and the + // selected Linode does not support Block Storage Encryption + + // Mock feature flag -- @TODO BSE: Remove feature flag once BSE is fully rolled out + mockAppendFeatureFlags({ + blockStorageEncryption: true, + }).as('getFeatureFlags'); + + // Mock account response + const mockAccount = accountFactory.build({ + capabilities: ['Linodes', 'Block Storage Encryption'], + }); + + mockGetAccount(mockAccount).as('getAccount'); + mockGetRegions(mockRegions).as('getRegions'); + + const linodeRequest = createLinodeRequestFactory.build({ + label: randomLabel(), + root_pass: randomString(16), + region: mockRegions[0].id, + booted: false, + }); + + cy.defer(() => createTestLinode(linodeRequest), 'creating Linode').then( + (linode: Linode) => { + cy.visitWithLogin('/volumes/create'); + cy.wait(['@getFeatureFlags', '@getAccount']); + + // Select a linode without the BSE capability + cy.findByLabelText('Linode') + .should('be.visible') + .click() + .type(linode.label); + + ui.autocompletePopper + .findByTitle(linode.label) + .should('be.visible') + .click(); + + // Check the "Encrypt Volume" checkbox + cy.get('[data-qa-checked]').should('be.visible').click(); + // }); + + // Ensure warning notice is displayed + cy.findByText(CLIENT_LIBRARY_UPDATE_COPY).should('be.visible'); + } + ); + }); + + /* + * - Checks for absence of Block Storage Encryption client library update notice on the Volume Create page + * when selected linode supports BSE + */ + it('does not display a warning notice on Volume Create page re: rebooting for client library updates when selected linode supports BSE', () => { + // Conditions: Block Storage encryption feature flag is on; user has Block Storage Encryption capability; volume being created is encrypted and the + // selected Linode supports Block Storage Encryption + + // Mock feature flag -- @TODO BSE: Remove feature flag once BSE is fully rolled out + mockAppendFeatureFlags({ + blockStorageEncryption: true, + }).as('getFeatureFlags'); + + // Mock account response + const mockAccount = accountFactory.build({ + capabilities: ['Linodes', 'Block Storage Encryption'], + }); + + // Mock linode + const mockLinode = linodeFactory.build({ + region: mockRegions[0].id, + id: 123456, + capabilities: ['blockstorage_encryption'], + }); + + mockGetAccount(mockAccount).as('getAccount'); + mockGetRegions(mockRegions).as('getRegions'); + mockGetLinodes([mockLinode]).as('getLinodes'); + mockGetLinodeDetails(mockLinode.id, mockLinode); + + cy.visitWithLogin(`/volumes/create`); + cy.wait(['@getAccount', '@getRegions', '@getLinodes']); + + // Select a linode without the BSE capability + cy.findByLabelText('Linode') + .should('be.visible') + .click() + .type(mockLinode.label); + + ui.autocompletePopper + .findByTitle(mockLinode.label) + .should('be.visible') + .click(); + + // Check the "Encrypt Volume" checkbox + cy.get('[data-qa-checked]').should('be.visible').click(); + // }); + + // Ensure warning notice is not displayed + cy.findByText(CLIENT_LIBRARY_UPDATE_COPY).should('not.exist'); + }); + + /* + * - Checks for Block Storage Encryption client library update notice in the Create/Attach Volume drawer from the 'Storage' details page of an existing Linode. */ it('displays a warning notice re: rebooting for client library updates under the appropriate conditions', () => { diff --git a/packages/manager/src/features/Volumes/VolumeCreate.tsx b/packages/manager/src/features/Volumes/VolumeCreate.tsx index 464c91f3e28..d1f4de80ca2 100644 --- a/packages/manager/src/features/Volumes/VolumeCreate.tsx +++ b/packages/manager/src/features/Volumes/VolumeCreate.tsx @@ -11,6 +11,7 @@ import { Button } from 'src/components/Button/Button'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { BLOCK_STORAGE_CHOOSE_REGION_COPY, + BLOCK_STORAGE_CLIENT_LIBRARY_UPDATE_REQUIRED_COPY, BLOCK_STORAGE_ENCRYPTION_GENERAL_DESCRIPTION, BLOCK_STORAGE_ENCRYPTION_OVERHEAD_CAVEAT, BLOCK_STORAGE_ENCRYPTION_UNAVAILABLE_IN_REGION_COPY, @@ -23,6 +24,7 @@ import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; import { Paper } from 'src/components/Paper'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; +import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; @@ -35,6 +37,7 @@ import { useAccountAgreements, useMutateAccountAgreements, } from 'src/queries/account/agreements'; +import { useLinodeQuery } from 'src/queries/linodes/linodes'; import { useGrants, useProfile } from 'src/queries/profile/profile'; import { useRegionsQuery } from 'src/queries/regions/regions'; import { @@ -240,6 +243,15 @@ export const VolumeCreate = () => { const { config_id, linode_id } = values; + const { data: linode } = useLinodeQuery( + linode_id ?? -1, + isBlockStorageEncryptionFeatureEnabled + ); + + const linodeSupportsBlockStorageEncryption = Boolean( + linode?.capabilities?.includes('blockstorage_encryption') + ); + const linodeError = touched.linode_id ? errors.linode_id : undefined; const { showGDPRCheckbox } = getGDPRDetails({ @@ -283,6 +295,11 @@ export const VolumeCreate = () => { } }; + const shouldDisplayClientLibraryCopy = + isBlockStorageEncryptionFeatureEnabled && + values.linode_id !== null && + !linodeSupportsBlockStorageEncryption; + return ( <> @@ -363,47 +380,57 @@ export const VolumeCreate = () => { )} - - { - const linodeRegion = linode.region; - const valuesRegion = values.region; - - /** When values.region is empty, all Linodes with - * block storage support will be displayed, regardless - * of their region. However, if a region is selected, - * only Linodes from the chosen region with block storage - * support will be shown. */ - return isNilOrEmpty(valuesRegion) - ? regionsWithBlockStorage.includes(linodeRegion) - : regionsWithBlockStorage.includes(linodeRegion) && - linodeRegion === valuesRegion; - }} - sx={{ - [theme.breakpoints.down('sm')]: { - width: 320, - }, - width: '400px', - }} - clearable - disabled={doesNotHavePermission} - errorText={linodeError} - onBlur={handleBlur} - onSelectionChange={handleLinodeChange} - value={values.linode_id} - /> - {renderSelectTooltip( - 'If you select a Linode, the Volume will be automatically created in that Linode’s region and attached upon creation.' - )} - + + + { + const linodeRegion = linode.region; + const valuesRegion = values.region; + + /** When values.region is empty, all Linodes with + * block storage support will be displayed, regardless + * of their region. However, if a region is selected, + * only Linodes from the chosen region with block storage + * support will be shown. */ + return isNilOrEmpty(valuesRegion) + ? regionsWithBlockStorage.includes(linodeRegion) + : regionsWithBlockStorage.includes(linodeRegion) && + linodeRegion === valuesRegion; + }} + sx={{ + [theme.breakpoints.down('sm')]: { + width: 320, + }, + width: '400px', + }} + clearable + disabled={doesNotHavePermission} + errorText={linodeError} + onBlur={handleBlur} + onSelectionChange={handleLinodeChange} + value={values.linode_id} + /> + {renderSelectTooltip( + 'If you select a Linode, the Volume will be automatically created in that Linode’s region and attached upon creation.' + )} + + {shouldDisplayClientLibraryCopy && + values.encryption === 'enabled' && ( + + + {BLOCK_STORAGE_CLIENT_LIBRARY_UPDATE_REQUIRED_COPY} + + + )} + {