Skip to content

Commit 235d4ae

Browse files
feat: [M3-10064] - Allow Node Pool Update Strategy to be configured when adding a Node Pool (linode#12631)
* initial clean up * loading state and fixes * don't show firewall for now * fix typecheck * add a cypress test * undo indenting changes but keep new test * fix spelling causing tests to fail * last test fix * Added changeset: Allow Node Pool Update Strategy to be configured when adding an enterprise node pool * Added changeset: Refactor the Add Node Pool drawer to use `react-hook-form` --------- Co-authored-by: Banks Nussman <[email protected]>
1 parent 00c55c3 commit 235d4ae

File tree

10 files changed

+241
-150
lines changed

10 files changed

+241
-150
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Tech Stories
3+
---
4+
5+
Refactor the Add Node Pool drawer to use `react-hook-form` ([#12631](https://github.com/linode/manager/pull/12631))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Allow Node Pool Update Strategy to be configured when adding an enterprise node pool ([#12631](https://github.com/linode/manager/pull/12631))

packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
mockGetLinodeTypes,
1515
} from 'support/intercepts/linodes';
1616
import {
17+
interceptCreateNodePool,
1718
mockAddNodePool,
1819
mockDeleteNodePool,
1920
mockGetApiEndpoints,
@@ -980,6 +981,85 @@ describe('LKE cluster updates', () => {
980981
ui.toast.assertMessage('Successfully reset Kubeconfig');
981982
});
982983

984+
it('can add a node pool with an update strategy on an LKE enterprise cluster', () => {
985+
const cluster = kubernetesClusterFactory.build({
986+
tier: 'enterprise',
987+
});
988+
const account = accountFactory.build({
989+
capabilities: ['Kubernetes Enterprise'],
990+
});
991+
const type = linodeTypeFactory.build({
992+
class: 'dedicated',
993+
label: 'Fake Plan',
994+
});
995+
996+
mockAppendFeatureFlags({
997+
lkeEnterprise: {
998+
enabled: true,
999+
postLa: true,
1000+
},
1001+
});
1002+
1003+
mockGetAccount(account).as('getAccount');
1004+
mockGetCluster(cluster).as('getCluster');
1005+
mockGetClusterPools(cluster.id, []).as('getNodePools');
1006+
mockGetLinodeTypes([type]).as('getTypes');
1007+
1008+
cy.visitWithLogin(`/kubernetes/clusters/${cluster.id}`);
1009+
1010+
cy.wait(['@getCluster', '@getNodePools', '@getAccount']);
1011+
1012+
ui.button
1013+
.findByTitle('Add a Node Pool')
1014+
.should('be.visible')
1015+
.should('be.enabled')
1016+
.click();
1017+
1018+
cy.wait('@getTypes');
1019+
1020+
// Selet a plan
1021+
cy.get(`[data-qa-plan-row="${type.label}"]`).within(() => {
1022+
// Increment nodes 3 times
1023+
cy.findByLabelText('Add 1').should('be.enabled').click();
1024+
cy.findByLabelText('Add 1').should('be.enabled').click();
1025+
cy.findByLabelText('Add 1').should('be.enabled').click();
1026+
});
1027+
1028+
interceptCreateNodePool(cluster.id).as('createNodePool');
1029+
1030+
ui.drawer.findByTitle(`Add a Node Pool: ${cluster.label}`).within(() => {
1031+
cy.findByLabelText('Update Strategy')
1032+
.should('be.visible')
1033+
.should('be.enabled')
1034+
.should('have.value', 'On Recycle Updates') // Should default to "On Recycle"
1035+
.click(); // Open the Autocomplete
1036+
1037+
ui.autocompletePopper
1038+
.findByTitle('Rolling Updates') // Select "Rolling Updates"
1039+
.should('be.visible')
1040+
.should('be.enabled')
1041+
.click();
1042+
1043+
// Verify the field's value actually changed
1044+
cy.findByLabelText('Update Strategy').should(
1045+
'have.value',
1046+
'Rolling Updates'
1047+
);
1048+
1049+
ui.button
1050+
.findByTitle('Add pool')
1051+
.should('be.enabled')
1052+
.should('be.visible')
1053+
.click();
1054+
});
1055+
1056+
cy.wait('@createNodePool').then((intercept) => {
1057+
const payload = intercept.request.body;
1058+
expect(payload.update_strategy).to.equal('rolling_update');
1059+
expect(payload.type).to.equal(type.id);
1060+
});
1061+
});
1062+
9831063
/*
9841064
* - Confirms UI flow when adding and deleting node pools.
9851065
* - Confirms that user cannot delete a node pool when there is only 1 pool.

packages/manager/cypress/support/intercepts/lke.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,17 @@ export const mockAddNodePool = (
254254
);
255255
};
256256

257+
/**
258+
* Intercepts POST request to create Node Pool.
259+
*
260+
* @returns Cypress chainable.
261+
*/
262+
export const interceptCreateNodePool = (
263+
clusterId: number
264+
): Cypress.Chainable<null> => {
265+
return cy.intercept('POST', apiMatcher(`lke/clusters/${clusterId}/pools`));
266+
};
267+
257268
/**
258269
* Intercepts PUT request to update a node pool and mocks the response.
259270
*

packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubernetesClusterDetail.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useAccount, useRegionsQuery } from '@linode/queries';
1+
import { useAccount } from '@linode/queries';
22
import { Box, CircleProgress, ErrorState, Notice, Stack } from '@linode/ui';
33
import { useLocation, useParams } from '@tanstack/react-router';
44
import * as React from 'react';
@@ -24,21 +24,9 @@ import { NodePoolsDisplay } from './NodePoolsDisplay/NodePoolsDisplay';
2424
import { UpgradeKubernetesClusterToHADialog } from './UpgradeClusterDialog';
2525
import UpgradeKubernetesVersionBanner from './UpgradeKubernetesVersionBanner';
2626

27-
const restrictedLkeNotice = (
28-
<Notice
29-
text={getRestrictedResourceText({
30-
action: 'edit',
31-
resourceType: 'LKE Clusters',
32-
isSingular: true,
33-
})}
34-
variant="warning"
35-
/>
36-
);
37-
3827
export const KubernetesClusterDetail = () => {
3928
const { data: account } = useAccount();
40-
const { clusterId } = useParams({ from: '/kubernetes/clusters/$clusterId' });
41-
const id = Number(clusterId);
29+
const { clusterId: id } = useParams({ from: '/kubernetes/clusters/$clusterId' });
4230
const location = useLocation();
4331
const { showAPL } = useAPLAvailability();
4432
const { isUsingBetaEndpoint } = useKubernetesBetaEndpoint();
@@ -51,7 +39,6 @@ export const KubernetesClusterDetail = () => {
5139
id,
5240
isUsingBetaEndpoint,
5341
});
54-
const { data: regionsData } = useRegionsQuery();
5542

5643
const { mutateAsync: updateKubernetesCluster } =
5744
useKubernetesClusterMutation(id);
@@ -114,7 +101,16 @@ export const KubernetesClusterDetail = () => {
114101
currentVersion={cluster?.k8s_version}
115102
/>
116103
)}
117-
{isClusterReadOnly && restrictedLkeNotice}
104+
{isClusterReadOnly && (
105+
<Notice
106+
text={getRestrictedResourceText({
107+
action: 'edit',
108+
resourceType: 'LKE Clusters',
109+
isSingular: true,
110+
})}
111+
variant="warning"
112+
/>
113+
)}
118114
<LandingHeader
119115
breadcrumbProps={{
120116
breadcrumbDataAttrs: { 'data-qa-breadcrumb': true },
@@ -159,7 +155,6 @@ export const KubernetesClusterDetail = () => {
159155
clusterRegionId={cluster.region}
160156
clusterTier={cluster.tier ?? 'standard'}
161157
isLkeClusterRestricted={isClusterReadOnly}
162-
regionsData={regionsData || []}
163158
/>
164159
</Stack>
165160
<UpgradeKubernetesClusterToHADialog

packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/AddNodePoolDrawer.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const props: Props = {
1313
clusterTier: 'standard',
1414
onClose: vi.fn(),
1515
open: true,
16-
regionsData: [],
1716
};
1817

1918
describe('AddNodePoolDrawer', () => {
@@ -34,6 +33,6 @@ describe('AddNodePoolDrawer', () => {
3433
<AddNodePoolDrawer {...props} clusterTier="enterprise" />
3534
);
3635

37-
await expect(queryByText('GPU')).toBeNull();
36+
expect(queryByText('GPU')).toBeNull();
3837
});
3938
});

0 commit comments

Comments
 (0)