Skip to content

Commit 9ff0e44

Browse files
authored
upcoming: [M3-9519] - Allow LKE-E IP ACL addresses to be optional with an explicit acknowledgement (#11856)
* Save working changes * Use try-catch to clean up schema validation * Clean up * Add unit test coverage for checkbox * Separate out test coverage for LKE-E ACL testing into the ACL section * Add checkbox to cluster details ACL drawer and update test coverage * Update copy to match * Remove the .only * Address feedback: don't alter section copy on check * Address feedback: Decrease padding for ErrorMessage * Fix incorrect conditional that was displaying wrong copy
1 parent 28f98cc commit 9ff0e44

File tree

12 files changed

+466
-93
lines changed

12 files changed

+466
-93
lines changed

packages/api-v4/src/kubernetes/kubernetes.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import {
2-
createKubeClusterSchema,
3-
createKubeEnterpriseClusterSchema,
4-
} from '@linode/validation/lib/kubernetes.schema';
1+
import { createKubeClusterSchema } from '@linode/validation/lib/kubernetes.schema';
52
import { API_ROOT, BETA_API_ROOT } from '../constants';
63
import Request, {
74
setData,
@@ -97,12 +94,7 @@ export const createKubernetesClusterBeta = (data: CreateKubeClusterPayload) => {
9794
return Request<KubernetesCluster>(
9895
setMethod('POST'),
9996
setURL(`${BETA_API_ROOT}/lke/clusters`),
100-
setData(
101-
data,
102-
data.tier === 'enterprise'
103-
? createKubeEnterpriseClusterSchema
104-
: createKubeClusterSchema
105-
)
97+
setData(data, createKubeClusterSchema)
10698
);
10799
};
108100

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

Lines changed: 238 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,223 @@ describe('LKE Cluster Creation with ACL', () => {
931931
.should('be.enabled');
932932
});
933933

934+
/**
935+
* - Confirms create flow for LKE-E cluster with ACL enabled by default
936+
* - Confirms at least one IP must be provided for ACL unless acknowledgement is checked
937+
* - Confirms the cluster details page shows ACL is enabled
938+
*/
939+
it('creates an LKE cluster with ACL enabled by default and handles IP address validation', () => {
940+
const clusterLabel = randomLabel();
941+
const mockedEnterpriseCluster = kubernetesClusterFactory.build({
942+
k8s_version: latestEnterpriseTierKubernetesVersion.id,
943+
label: clusterLabel,
944+
region: 'us-iad',
945+
tier: 'enterprise',
946+
});
947+
const mockedEnterpriseClusterPools = [nanodeMemoryPool];
948+
const mockACL = kubernetesControlPlaneACLFactory.build({
949+
acl: {
950+
addresses: {
951+
ipv4: [],
952+
ipv6: [],
953+
},
954+
enabled: true,
955+
'revision-id': '',
956+
},
957+
});
958+
mockGetControlPlaneACL(mockedEnterpriseCluster.id, mockACL).as(
959+
'getControlPlaneACL'
960+
);
961+
mockGetAccount(
962+
accountFactory.build({
963+
capabilities: [
964+
'Kubernetes Enterprise',
965+
'LKE HA Control Planes',
966+
'LKE Network Access Control List (IP ACL)',
967+
],
968+
})
969+
).as('getAccount');
970+
mockGetTieredKubernetesVersions('enterprise', [
971+
latestEnterpriseTierKubernetesVersion,
972+
]).as('getTieredKubernetesVersions');
973+
mockGetKubernetesVersions([latestKubernetesVersion]).as(
974+
'getKubernetesVersions'
975+
);
976+
mockGetLinodeTypes(mockedLKEClusterTypes).as('getLinodeTypes');
977+
mockGetLKEClusterTypes(mockedLKEEnterprisePrices).as(
978+
'getLKEEnterpriseClusterTypes'
979+
);
980+
mockGetRegions([
981+
regionFactory.build({
982+
capabilities: ['Linodes', 'Kubernetes', 'Kubernetes Enterprise'],
983+
id: 'us-iad',
984+
label: 'Washington, DC',
985+
}),
986+
]).as('getRegions');
987+
mockGetCluster(mockedEnterpriseCluster).as('getCluster');
988+
mockCreateCluster(mockedEnterpriseCluster).as('createCluster');
989+
mockGetClusters([mockedEnterpriseCluster]).as('getClusters');
990+
mockGetClusterPools(
991+
mockedEnterpriseCluster.id,
992+
mockedEnterpriseClusterPools
993+
).as('getClusterPools');
994+
mockGetDashboardUrl(mockedEnterpriseCluster.id).as('getDashboardUrl');
995+
mockGetApiEndpoints(mockedEnterpriseCluster.id).as('getApiEndpoints');
996+
997+
cy.visitWithLogin('/kubernetes/clusters');
998+
cy.wait(['@getAccount']);
999+
1000+
ui.button
1001+
.findByTitle('Create Cluster')
1002+
.should('be.visible')
1003+
.should('be.enabled')
1004+
.click();
1005+
1006+
cy.url().should('endWith', '/kubernetes/create');
1007+
cy.wait(['@getKubernetesVersions', '@getTieredKubernetesVersions']);
1008+
1009+
// Select enterprise tier.
1010+
cy.get(`[data-qa-select-card-heading="LKE Enterprise"]`)
1011+
.closest('[data-qa-selection-card]')
1012+
.click();
1013+
1014+
cy.wait(['@getLKEEnterpriseClusterTypes', '@getRegions']);
1015+
1016+
// Select a supported region.
1017+
ui.regionSelect.find().clear().type('Washington, DC{enter}');
1018+
1019+
// Select an enterprise version.
1020+
ui.autocomplete
1021+
.findByLabel('Kubernetes Version')
1022+
.should('be.visible')
1023+
.click();
1024+
1025+
clusterPlans.forEach((clusterPlan) => {
1026+
const nodeCount = clusterPlan.nodeCount;
1027+
const planName = clusterPlan.planName;
1028+
// Click the right tab for the plan, and add a node pool with the desired
1029+
// number of nodes.
1030+
cy.findByText(clusterPlan.tab).should('be.visible').click();
1031+
const quantityInput = '[name="Quantity"]';
1032+
cy.findByText(planName)
1033+
.should('be.visible')
1034+
.closest('tr')
1035+
.within(() => {
1036+
cy.get(quantityInput).should('be.visible');
1037+
cy.get(quantityInput).click();
1038+
cy.get(quantityInput).type(`{selectall}${nodeCount}`);
1039+
1040+
ui.button
1041+
.findByTitle('Add')
1042+
.should('be.visible')
1043+
.should('be.enabled')
1044+
.click();
1045+
});
1046+
});
1047+
1048+
// Confirm ACL is enabled by default.
1049+
cy.contains('Control Plane ACL').should('be.visible');
1050+
ui.toggle
1051+
.find()
1052+
.should('have.attr', 'data-qa-toggle', 'true')
1053+
.should('be.visible');
1054+
cy.findByRole('checkbox', { name: /Provide an ACL later/ }).should(
1055+
'not.be.checked'
1056+
);
1057+
1058+
// Try to submit the form without the ACL acknowledgement checked.
1059+
cy.get('[data-testid="kube-checkout-bar"]')
1060+
.should('be.visible')
1061+
.within(() => {
1062+
ui.button
1063+
.findByTitle('Create Cluster')
1064+
.should('be.visible')
1065+
.should('be.enabled')
1066+
.click();
1067+
});
1068+
1069+
// Confirm error validation requires an ACL IP.
1070+
cy.findByText(
1071+
'At least one IP address or CIDR range is required for LKE Enterprise.'
1072+
).should('be.visible');
1073+
1074+
// Add an IP,
1075+
cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
1076+
.should('be.visible')
1077+
.click();
1078+
cy.focused().clear();
1079+
cy.focused().type('10.0.0.0/24');
1080+
1081+
cy.get('[data-testid="kube-checkout-bar"]')
1082+
.should('be.visible')
1083+
.within(() => {
1084+
// Try to submit the form again.
1085+
ui.button
1086+
.findByTitle('Create Cluster')
1087+
.should('be.visible')
1088+
.should('be.enabled')
1089+
.click();
1090+
});
1091+
1092+
// Confirm the validation message is gone.
1093+
cy.findByText(
1094+
'At least one IP address or CIDR range is required for LKE Enterprise.'
1095+
).should('not.exist');
1096+
1097+
// Check the acknowledgement to prevent IP validation.
1098+
cy.findByRole('checkbox', { name: /Provide an ACL later/ }).check();
1099+
1100+
// Clear the IP address field and check the acknowledgement to confirm the form can now submit without IP address validation.
1101+
cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
1102+
.should('be.visible')
1103+
.click();
1104+
cy.focused().clear();
1105+
cy.findByRole('checkbox', { name: /Provide an ACL later/ }).check();
1106+
1107+
// Finally, add a label, so the form will submit.
1108+
cy.findByLabelText('Cluster Label').should('be.visible').click();
1109+
cy.focused().type(`${clusterLabel}{enter}`);
1110+
1111+
cy.get('[data-testid="kube-checkout-bar"]')
1112+
.should('be.visible')
1113+
.within(() => {
1114+
// Try to submit the form.
1115+
ui.button
1116+
.findByTitle('Create Cluster')
1117+
.should('be.visible')
1118+
.should('be.enabled')
1119+
.click();
1120+
});
1121+
1122+
// Confirm the validation message is gone.
1123+
cy.findByText(
1124+
'At least one IP address or CIDR range is required for LKE Enterprise.'
1125+
).should('not.exist');
1126+
1127+
cy.wait([
1128+
'@getCluster',
1129+
'@getClusterPools',
1130+
'@createCluster',
1131+
'@getLKEEnterpriseClusterTypes',
1132+
'@getLinodeTypes',
1133+
'@getDashboardUrl',
1134+
'@getApiEndpoints',
1135+
'@getControlPlaneACL',
1136+
]);
1137+
1138+
cy.url().should(
1139+
'endWith',
1140+
`/kubernetes/clusters/${mockedEnterpriseCluster.id}/summary`
1141+
);
1142+
1143+
// Confirms Summary panel displays as expected
1144+
cy.contains('Control Plane ACL').should('be.visible');
1145+
ui.button
1146+
.findByTitle('Enabled (0 IP Addresses)')
1147+
.should('be.visible')
1148+
.should('be.enabled');
1149+
});
1150+
9341151
/**
9351152
* - Confirms IP validation error appears when a bad IP is entered
9361153
* - Confirms IP validation error disappears when a valid IP is entered
@@ -1089,7 +1306,7 @@ describe('LKE Cluster Creation with LKE-E', () => {
10891306
* - Confirms an LKE-E supported region can be selected
10901307
* - Confirms an LKE-E supported k8 version can be selected
10911308
* - Confirms the APL section is disabled while it remains unsupported
1092-
* - Confirms at least one IP must be provided for ACL
1309+
* - Confirms ACL is enabled by default
10931310
* - Confirms the checkout bar displays the correct LKE-E info
10941311
* - Confirms an enterprise cluster can be created with the correct chip, version, and price
10951312
* - Confirms that the total node count for each pool is displayed
@@ -1103,7 +1320,20 @@ describe('LKE Cluster Creation with LKE-E', () => {
11031320
tier: 'enterprise',
11041321
});
11051322
const mockedEnterpriseClusterPools = [nanodeMemoryPool, dedicatedCpuPool];
1323+
const mockACL = kubernetesControlPlaneACLFactory.build({
1324+
acl: {
1325+
addresses: {
1326+
ipv4: ['10.0.0.0/24'],
1327+
ipv6: [],
1328+
},
1329+
enabled: true,
1330+
'revision-id': '',
1331+
},
1332+
});
11061333

1334+
mockGetControlPlaneACL(mockedEnterpriseCluster.id, mockACL).as(
1335+
'getControlPlaneACL'
1336+
);
11071337
mockGetAccountBeta(
11081338
accountBetaFactory.build({
11091339
id: 'apl',
@@ -1287,19 +1517,14 @@ describe('LKE Cluster Creation with LKE-E', () => {
12871517
cy.findByText('Linode 2 GB Plan').should('be.visible');
12881518
cy.findByText('$15.00').should('be.visible');
12891519
cy.findByText('$459.00').should('be.visible');
1290-
1291-
// Try to submit the form
1292-
ui.button
1293-
.findByTitle('Create Cluster')
1294-
.should('be.visible')
1295-
.should('be.enabled')
1296-
.click();
12971520
});
12981521

1299-
// Confirm error validation requires an ACL IP
1300-
cy.findByText(
1301-
'At least one IP address or CIDR range is required for LKE Enterprise.'
1302-
).should('be.visible');
1522+
// Confirms ACL is enabled by default.
1523+
cy.contains('Control Plane ACL').should('be.visible');
1524+
ui.toggle
1525+
.find()
1526+
.should('have.attr', 'data-qa-toggle', 'true')
1527+
.should('be.visible');
13031528

13041529
// Add an IP
13051530
cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
@@ -1319,10 +1544,6 @@ describe('LKE Cluster Creation with LKE-E', () => {
13191544
.click();
13201545
});
13211546

1322-
cy.findByText(
1323-
'At least one IP address or CIDR range is required for LKE Enterprise.'
1324-
).should('not.exist');
1325-
13261547
// Wait for LKE cluster to be created and confirm that we are redirected
13271548
// to the cluster summary page.
13281549
cy.wait([
@@ -1333,6 +1554,7 @@ describe('LKE Cluster Creation with LKE-E', () => {
13331554
'@getLinodeTypes',
13341555
'@getDashboardUrl',
13351556
'@getApiEndpoints',
1557+
'@getControlPlaneACL',
13361558
]);
13371559

13381560
cy.url().should(

0 commit comments

Comments
 (0)