From 6901ea33d82aa6c62c94c336c20314be2d1a1440 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 16:56:48 -0500 Subject: [PATCH 1/8] initial fix --- .../features/Linodes/LinodeCreate/Region.tsx | 21 ++++++++++++------- .../Linodes/LinodeCreate/Security.tsx | 6 ++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index 73fa3c83fd3..f6a7d73f9d0 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -139,14 +139,19 @@ export const Region = React.memo(() => { } if (isDiskEncryptionFeatureEnabled) { - // Enable disk encryption by default if the region supports it - const defaultDiskEncryptionValue = region.capabilities.includes( - 'Disk Encryption' - ) - ? 'enabled' - : undefined; - - setValue('disk_encryption', defaultDiskEncryptionValue); + if (region.site_type === 'distributed') { + // If a distributed region is selected, make sure we don't send disk_encryption in the payload. + setValue('disk_encryption', undefined); + } else { + // Enable disk encryption by default if the region supports it + const defaultDiskEncryptionValue = region.capabilities.includes( + 'Disk Encryption' + ) + ? 'enabled' + : undefined; + + setValue('disk_encryption', defaultDiskEncryptionValue); + } } if (!isLabelFieldDirty) { diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx index 2a5c39c0f20..eb36828828c 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx @@ -107,12 +107,14 @@ export const Security = () => { ? DISK_ENCRYPTION_DEFAULT_DISTRIBUTED_INSTANCES : DISK_ENCRYPTION_UNAVAILABLE_IN_REGION_COPY } + isEncryptEntityChecked={ + isDistributedRegion || field.value === 'enabled' + } onChange={(checked) => field.onChange(checked ? 'enabled' : 'disabled') } - disabled={!regionSupportsDiskEncryption} + disabled={isDistributedRegion || !regionSupportsDiskEncryption} error={fieldState.error?.message} - isEncryptEntityChecked={field.value === 'enabled'} /> )} control={control} From b5e3b7bf4bdf0b1a465c01001e49088528bc0d9d Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 16:59:49 -0500 Subject: [PATCH 2/8] fix other component --- packages/manager/src/components/Encryption/Encryption.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/components/Encryption/Encryption.tsx b/packages/manager/src/components/Encryption/Encryption.tsx index 1b90722ce39..84daa821289 100644 --- a/packages/manager/src/components/Encryption/Encryption.tsx +++ b/packages/manager/src/components/Encryption/Encryption.tsx @@ -72,7 +72,7 @@ export const Encryption = (props: EncryptionProps) => { flexDirection="row" > onChange(checked)} From 5efc462693512562b5b33ed65fae0e0195599864 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 17:09:32 -0500 Subject: [PATCH 3/8] also fix the rebuild dialog --- packages/manager/src/components/AccessPanel/AccessPanel.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/manager/src/components/AccessPanel/AccessPanel.tsx b/packages/manager/src/components/AccessPanel/AccessPanel.tsx index 0f87f6e16c9..3a9bf425801 100644 --- a/packages/manager/src/components/AccessPanel/AccessPanel.tsx +++ b/packages/manager/src/components/AccessPanel/AccessPanel.tsx @@ -218,7 +218,9 @@ export const AccessPanel = (props: Props) => { linodeIsInDistributedRegion, regionSupportsDiskEncryption, })} - isEncryptEntityChecked={diskEncryptionEnabled ?? false} + isEncryptEntityChecked={ + linodeIsInDistributedRegion || (diskEncryptionEnabled ?? false) + } onChange={() => toggleDiskEncryptionEnabled()} /> From 6b3e7e95a8892d0108b694c8fd5c7f4ea2f03d98 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 17:21:28 -0500 Subject: [PATCH 4/8] bump version and add changeset --- packages/manager/CHANGELOG.md | 6 ++++++ packages/manager/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index 867ad0a063e..c422e9236ae 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [2025-02-27] - v1.137.2 + +### Fixed: + +- Disk Encryption logic preventing Linode deployment in distributed regions ([#11760](https://github.com/linode/manager/pull/11760) + ## [2025-02-25] - v1.137.1 ### Fixed: diff --git a/packages/manager/package.json b/packages/manager/package.json index 0ccb21757a3..2f7f2218c2b 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -2,7 +2,7 @@ "name": "linode-manager", "author": "Linode", "description": "The Linode Manager website", - "version": "1.137.1", + "version": "1.137.2", "private": true, "type": "module", "bugs": { From d25b5f133bda3605d9dc67214ecab190b86b02fc Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 17:32:02 -0500 Subject: [PATCH 5/8] ensure summary reflects encrypted status --- .../LinodeCreate/Summary/Summary.test.tsx | 17 +++++++++++++++++ .../Linodes/LinodeCreate/Summary/Summary.tsx | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.test.tsx index dc151f343a1..156dad3ed8d 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.test.tsx @@ -264,4 +264,21 @@ describe('Linode Create Summary', () => { await findByText(`5 Nodes - $10/month $2.50/hr`); }); + + it('should render "Encrypted" if a distributed region is selected', async () => { + const region = regionFactory.build({ site_type: 'distributed' }); + + server.use( + http.get('*/v4/regions', () => { + return HttpResponse.json(makeResourcePage([region])); + }) + ); + + const { findByText } = renderWithThemeAndHookFormContext({ + component: , + useFormOptions: { defaultValues: { region: region.id } }, + }); + + await findByText('Encrypted'); + }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx index 8d3eead884a..c06fdeaecc6 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx @@ -125,7 +125,7 @@ export const Summary = () => { item: { title: 'Encrypted', }, - show: diskEncryption === 'enabled', + show: diskEncryption === 'enabled' || region?.site_type === 'distributed', }, ]; From 2d951c89b5058b918ace5112b0bb1b67a82abb51 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 18:04:39 -0500 Subject: [PATCH 6/8] add unit test --- .../Linodes/LinodeCreate/Security.test.tsx | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx index 48ff951ef21..604fce4f4ca 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx @@ -130,9 +130,10 @@ describe('Security', () => { expect(heading.tagName).toBe('H3'); }); - it('should disable disk encryption if the selected region does not support it', async () => { + it('should disable disk encryption if the selected core region does not support it', async () => { const region = regionFactory.build({ capabilities: [], + site_type: 'core', }); const account = accountFactory.build({ capabilities: ['Disk Encryption'] }); @@ -158,4 +159,39 @@ describe('Security', () => { 'Disk encryption is not available in the selected region. Select another region to use Disk Encryption.' ); }); + + it('should disable the disk encryption checkbox (but show it as enabled) if the selected region is a distributed region', async () => { + const region = regionFactory.build({ + capabilities: [], + site_type: 'distributed', + }); + + const account = accountFactory.build({ capabilities: ['Disk Encryption'] }); + + server.use( + http.get('*/v4/account', () => { + return HttpResponse.json(account); + }), + http.get('*/v4/regions', () => { + return HttpResponse.json(makeResourcePage([region])); + }) + ); + + const { + findByLabelText, + getByLabelText, + } = renderWithThemeAndHookFormContext({ + component: , + options: { flags: { linodeDiskEncryption: true } }, + useFormOptions: { defaultValues: { region: region.id } }, + }); + + await findByLabelText( + 'Distributed Compute Instances are encrypted. This setting can not be changed.' + ); + + const checkbox = getByLabelText('Encrypt Disk'); + + expect(checkbox).toBeDisabled(); + }); }); From c5fa01a1000f79ccbea7371ec162d9377e0102d7 Mon Sep 17 00:00:00 2001 From: Banks Nussman Date: Thu, 27 Feb 2025 18:07:12 -0500 Subject: [PATCH 7/8] improve unit test --- .../src/features/Linodes/LinodeCreate/Security.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx index 604fce4f4ca..988f665f38b 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Security.test.tsx @@ -162,7 +162,7 @@ describe('Security', () => { it('should disable the disk encryption checkbox (but show it as enabled) if the selected region is a distributed region', async () => { const region = regionFactory.build({ - capabilities: [], + capabilities: ['Disk Encryption'], site_type: 'distributed', }); @@ -192,6 +192,7 @@ describe('Security', () => { const checkbox = getByLabelText('Encrypt Disk'); + expect(checkbox).toBeChecked(); expect(checkbox).toBeDisabled(); }); }); From f97ae2c2e50882180b69c4f7333b2a839e2d4c9a Mon Sep 17 00:00:00 2001 From: Joe D'Amore Date: Thu, 27 Feb 2025 18:30:43 -0500 Subject: [PATCH 8/8] Add integration tests to cover Linode Create disk encryption flow for Distributed Regions --- ...create-linode-with-disk-encryption.spec.ts | 139 +++++++++++++++++- .../features/Linodes/LinodeCreate/Region.tsx | 2 +- .../Linodes/LinodeCreate/TwoStepRegion.tsx | 2 +- 3 files changed, 139 insertions(+), 4 deletions(-) diff --git a/packages/manager/cypress/e2e/core/linodes/create-linode-with-disk-encryption.spec.ts b/packages/manager/cypress/e2e/core/linodes/create-linode-with-disk-encryption.spec.ts index 08d178ccd06..72ac16d81a2 100644 --- a/packages/manager/cypress/e2e/core/linodes/create-linode-with-disk-encryption.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/create-linode-with-disk-encryption.spec.ts @@ -1,6 +1,14 @@ import { ui } from 'support/ui'; -import { accountFactory, regionFactory } from '@src/factories'; -import { mockGetRegions } from 'support/intercepts/regions'; +import { + linodeFactory, + accountFactory, + linodeTypeFactory, + regionFactory, +} from '@src/factories'; +import { + mockGetRegionAvailability, + mockGetRegions, +} from 'support/intercepts/regions'; import { mockGetAccount } from 'support/intercepts/account'; import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags'; import { makeFeatureFlagData } from 'support/util/feature-flags'; @@ -8,6 +16,14 @@ import { checkboxTestId, headerTestId, } from 'src/components/Encryption/constants'; +import { extendRegion } from 'support/util/regions'; +import { linodeCreatePage } from 'support/ui/pages'; +import { + mockCreateLinode, + mockGetLinodeTypes, +} from 'support/intercepts/linodes'; +import { randomLabel, randomString } from 'support/util/random'; +import type { Region } from '@linode/api-v4'; describe('Create Linode with Disk Encryption', () => { it('should not have a "Disk Encryption" section visible if the feature flag is off and user does not have capability', () => { @@ -77,4 +93,123 @@ describe('Create Linode with Disk Encryption', () => { cy.get(`[data-testid="${checkboxTestId}"]`).should('be.enabled'); }); + + // Confirm Linode Disk Encryption features when using Distributed Regions. + describe('Distributed regions', () => { + const encryptionTooltipMessage = + 'Distributed Compute Instances are encrypted. This setting can not be changed.'; + + const mockDistributedRegionWithoutCapability = regionFactory.build({ + capabilities: [ + 'Linodes', + 'Cloud Firewall', + 'Distributed Plans', + 'Placement Group', + ], + site_type: 'distributed', + }); + + const mockDistributedRegionWithCapability = regionFactory.build({ + capabilities: [ + 'Linodes', + 'Cloud Firewall', + 'Distributed Plans', + 'Placement Group', + 'Disk Encryption', + ], + site_type: 'distributed', + }); + + const mockDistributedRegions: Region[] = [ + mockDistributedRegionWithCapability, + mockDistributedRegionWithoutCapability, + ]; + + const mockLinodeType = linodeTypeFactory.build({ + id: 'nanode-edge-1', + label: 'Nanode 1GB', + class: 'nanode', + }); + + /* + * Right now there's some ambiguity over the 'Disk Encryption' capability + * and whether it's expected to be present for Distributed Regions. We'll + * test Cloud against both scenarios -- when distributed regions do and do + * not have the capability -- and confirm that the Linode Create flow works + * as expected in both cases. + */ + mockDistributedRegions.forEach((distributedRegion) => { + const suffix = distributedRegion.capabilities.includes('Disk Encryption') + ? '(with region capability)' + : '(without region capability)'; + + /* + * - Confirms that disk encryption works as expected for distributed regions. Specifically: + * - Encrypted checkbox is always checked, is disabled, and therefore cannot be changed. + * - Outgoing Linode create API request payload does NOT contain encryption property. + */ + it(`creates a Linode with Disk Encryption in a distributed region ${suffix}`, () => { + const mockRegions = [distributedRegion]; + const mockLinode = linodeFactory.build({ + label: randomLabel(), + region: distributedRegion.id, + }); + + mockAppendFeatureFlags({ + gecko2: { + enabled: true, + }, + }); + + mockGetRegions(mockRegions); + mockGetLinodeTypes([mockLinodeType]); + mockGetRegionAvailability(distributedRegion.id, []); + mockCreateLinode(mockLinode).as('createLinode'); + cy.visitWithLogin('/linodes/create'); + + cy.get('[data-qa-linode-region]').within(() => { + ui.tabList.find().within(() => { + cy.findByText('Distributed').click(); + }); + + cy.findByLabelText('Region').type(distributedRegion.label); + ui.regionSelect + .findItemByRegionLabel( + extendRegion(distributedRegion).label, + mockRegions + ) + .click(); + }); + + linodeCreatePage.setLabel(mockLinode.label); + linodeCreatePage.setRootPassword(randomString(32)); + + // Select mock Nanode plan type. + cy.get('[data-qa-plan-row="Nanode 1 GB"]').click(); + + cy.findByLabelText('Encrypt Disk') + .should('be.disabled') + .should('be.checked'); + + cy.findByLabelText(encryptionTooltipMessage).click(); + ui.tooltip.findByText(encryptionTooltipMessage).should('be.visible'); + + // Click "Create Linode" and confirm outgoing API request payload. + ui.button + .findByTitle('Create Linode') + .should('be.visible') + .should('be.enabled') + .click(); + + // Submit form to create Linode and confirm that outgoing API request + // contains expected user data. + cy.wait('@createLinode').then((xhr) => { + const requestPayload = xhr.request.body; + const regionId = requestPayload['region']; + expect(regionId).to.equal(mockLinode.region); + expect(requestPayload['disk_encryption']).to.be.undefined; + }); + }); + }); + }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index f6a7d73f9d0..decfebeb1eb 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -213,7 +213,7 @@ export const Region = React.memo(() => { } return ( - + Region { const { params } = useLinodeCreateQueryParams(); return ( - + Region