Skip to content
Merged
12 changes: 2 additions & 10 deletions packages/api-v4/src/kubernetes/kubernetes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
createKubeClusterSchema,
createKubeEnterpriseClusterSchema,
} from '@linode/validation/lib/kubernetes.schema';
import { createKubeClusterSchema } from '@linode/validation/lib/kubernetes.schema';
import { API_ROOT, BETA_API_ROOT } from '../constants';
import Request, {
setData,
Expand Down Expand Up @@ -97,12 +94,7 @@ export const createKubernetesClusterBeta = (data: CreateKubeClusterPayload) => {
return Request<KubernetesCluster>(
setMethod('POST'),
setURL(`${BETA_API_ROOT}/lke/clusters`),
setData(
data,
data.tier === 'enterprise'
? createKubeEnterpriseClusterSchema
: createKubeClusterSchema
)
setData(data, createKubeClusterSchema)
);
};

Expand Down
254 changes: 238 additions & 16 deletions packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,223 @@ describe('LKE Cluster Creation with ACL', () => {
.should('be.enabled');
});

/**
* - Confirms create flow for LKE-E cluster with ACL enabled by default
* - Confirms at least one IP must be provided for ACL unless acknowledgement is checked
* - Confirms the cluster details page shows ACL is enabled
*/
it('creates an LKE cluster with ACL enabled by default and handles IP address validation', () => {
const clusterLabel = randomLabel();
const mockedEnterpriseCluster = kubernetesClusterFactory.build({
k8s_version: latestEnterpriseTierKubernetesVersion.id,
label: clusterLabel,
region: 'us-iad',
tier: 'enterprise',
});
const mockedEnterpriseClusterPools = [nanodeMemoryPool];
const mockACL = kubernetesControlPlaneACLFactory.build({
acl: {
addresses: {
ipv4: [],
ipv6: [],
},
enabled: true,
'revision-id': '',
},
});
mockGetControlPlaneACL(mockedEnterpriseCluster.id, mockACL).as(
'getControlPlaneACL'
);
mockGetAccount(
accountFactory.build({
capabilities: [
'Kubernetes Enterprise',
'LKE HA Control Planes',
'LKE Network Access Control List (IP ACL)',
],
})
).as('getAccount');
mockGetTieredKubernetesVersions('enterprise', [
latestEnterpriseTierKubernetesVersion,
]).as('getTieredKubernetesVersions');
mockGetKubernetesVersions([latestKubernetesVersion]).as(
'getKubernetesVersions'
);
mockGetLinodeTypes(mockedLKEClusterTypes).as('getLinodeTypes');
mockGetLKEClusterTypes(mockedLKEEnterprisePrices).as(
'getLKEEnterpriseClusterTypes'
);
mockGetRegions([
regionFactory.build({
capabilities: ['Linodes', 'Kubernetes', 'Kubernetes Enterprise'],
id: 'us-iad',
label: 'Washington, DC',
}),
]).as('getRegions');
mockGetCluster(mockedEnterpriseCluster).as('getCluster');
mockCreateCluster(mockedEnterpriseCluster).as('createCluster');
mockGetClusters([mockedEnterpriseCluster]).as('getClusters');
mockGetClusterPools(
mockedEnterpriseCluster.id,
mockedEnterpriseClusterPools
).as('getClusterPools');
mockGetDashboardUrl(mockedEnterpriseCluster.id).as('getDashboardUrl');
mockGetApiEndpoints(mockedEnterpriseCluster.id).as('getApiEndpoints');

cy.visitWithLogin('/kubernetes/clusters');
cy.wait(['@getAccount']);

ui.button
.findByTitle('Create Cluster')
.should('be.visible')
.should('be.enabled')
.click();

cy.url().should('endWith', '/kubernetes/create');
cy.wait(['@getKubernetesVersions', '@getTieredKubernetesVersions']);

// Select enterprise tier.
cy.get(`[data-qa-select-card-heading="LKE Enterprise"]`)
.closest('[data-qa-selection-card]')
.click();

cy.wait(['@getLKEEnterpriseClusterTypes', '@getRegions']);

// Select a supported region.
ui.regionSelect.find().clear().type('Washington, DC{enter}');

// Select an enterprise version.
ui.autocomplete
.findByLabel('Kubernetes Version')
.should('be.visible')
.click();

clusterPlans.forEach((clusterPlan) => {
const nodeCount = clusterPlan.nodeCount;
const planName = clusterPlan.planName;
// Click the right tab for the plan, and add a node pool with the desired
// number of nodes.
cy.findByText(clusterPlan.tab).should('be.visible').click();
const quantityInput = '[name="Quantity"]';
cy.findByText(planName)
.should('be.visible')
.closest('tr')
.within(() => {
cy.get(quantityInput).should('be.visible');
cy.get(quantityInput).click();
cy.get(quantityInput).type(`{selectall}${nodeCount}`);

ui.button
.findByTitle('Add')
.should('be.visible')
.should('be.enabled')
.click();
});
});

// Confirm ACL is enabled by default.
cy.contains('Control Plane ACL').should('be.visible');
ui.toggle
.find()
.should('have.attr', 'data-qa-toggle', 'true')
.should('be.visible');
cy.findByRole('checkbox', { name: /Provide an ACL later/ }).should(
'not.be.checked'
);

// Try to submit the form without the ACL acknowledgement checked.
cy.get('[data-testid="kube-checkout-bar"]')
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Create Cluster')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm error validation requires an ACL IP.
cy.findByText(
'At least one IP address or CIDR range is required for LKE Enterprise.'
).should('be.visible');

// Add an IP,
cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
.should('be.visible')
.click();
cy.focused().clear();
cy.focused().type('10.0.0.0/24');

cy.get('[data-testid="kube-checkout-bar"]')
.should('be.visible')
.within(() => {
// Try to submit the form again.
ui.button
.findByTitle('Create Cluster')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm the validation message is gone.
cy.findByText(
'At least one IP address or CIDR range is required for LKE Enterprise.'
).should('not.exist');

// Check the acknowledgement to prevent IP validation.
cy.findByRole('checkbox', { name: /Provide an ACL later/ }).check();

// Clear the IP address field and check the acknowledgement to confirm the form can now submit without IP address validation.
cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
.should('be.visible')
.click();
cy.focused().clear();
cy.findByRole('checkbox', { name: /Provide an ACL later/ }).check();

// Finally, add a label, so the form will submit.
cy.findByLabelText('Cluster Label').should('be.visible').click();
cy.focused().type(`${clusterLabel}{enter}`);

cy.get('[data-testid="kube-checkout-bar"]')
.should('be.visible')
.within(() => {
// Try to submit the form.
ui.button
.findByTitle('Create Cluster')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm the validation message is gone.
cy.findByText(
'At least one IP address or CIDR range is required for LKE Enterprise.'
).should('not.exist');

cy.wait([
'@getCluster',
'@getClusterPools',
'@createCluster',
'@getLKEEnterpriseClusterTypes',
'@getLinodeTypes',
'@getDashboardUrl',
'@getApiEndpoints',
'@getControlPlaneACL',
]);

cy.url().should(
'endWith',
`/kubernetes/clusters/${mockedEnterpriseCluster.id}/summary`
);

// Confirms Summary panel displays as expected
cy.contains('Control Plane ACL').should('be.visible');
ui.button
.findByTitle('Enabled (0 IP Addresses)')
.should('be.visible')
.should('be.enabled');
});

/**
* - Confirms IP validation error appears when a bad IP is entered
* - Confirms IP validation error disappears when a valid IP is entered
Expand Down Expand Up @@ -1089,7 +1306,7 @@ describe('LKE Cluster Creation with LKE-E', () => {
* - Confirms an LKE-E supported region can be selected
* - Confirms an LKE-E supported k8 version can be selected
* - Confirms the APL section is disabled while it remains unsupported
* - Confirms at least one IP must be provided for ACL
* - Confirms ACL is enabled by default
* - Confirms the checkout bar displays the correct LKE-E info
* - Confirms an enterprise cluster can be created with the correct chip, version, and price
* - Confirms that the total node count for each pool is displayed
Expand All @@ -1103,7 +1320,20 @@ describe('LKE Cluster Creation with LKE-E', () => {
tier: 'enterprise',
});
const mockedEnterpriseClusterPools = [nanodeMemoryPool, dedicatedCpuPool];
const mockACL = kubernetesControlPlaneACLFactory.build({
acl: {
addresses: {
ipv4: ['10.0.0.0/24'],
ipv6: [],
},
enabled: true,
'revision-id': '',
},
});

mockGetControlPlaneACL(mockedEnterpriseCluster.id, mockACL).as(
'getControlPlaneACL'
);
mockGetAccountBeta(
accountBetaFactory.build({
id: 'apl',
Expand Down Expand Up @@ -1287,19 +1517,14 @@ describe('LKE Cluster Creation with LKE-E', () => {
cy.findByText('Linode 2 GB Plan').should('be.visible');
cy.findByText('$15.00').should('be.visible');
cy.findByText('$459.00').should('be.visible');

// Try to submit the form
ui.button
.findByTitle('Create Cluster')
.should('be.visible')
.should('be.enabled')
.click();
});

// Confirm error validation requires an ACL IP
cy.findByText(
'At least one IP address or CIDR range is required for LKE Enterprise.'
).should('be.visible');
// Confirms ACL is enabled by default.
cy.contains('Control Plane ACL').should('be.visible');
ui.toggle
.find()
.should('have.attr', 'data-qa-toggle', 'true')
.should('be.visible');

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

cy.findByText(
'At least one IP address or CIDR range is required for LKE Enterprise.'
).should('not.exist');

// Wait for LKE cluster to be created and confirm that we are redirected
// to the cluster summary page.
cy.wait([
Expand All @@ -1333,6 +1554,7 @@ describe('LKE Cluster Creation with LKE-E', () => {
'@getLinodeTypes',
'@getDashboardUrl',
'@getApiEndpoints',
'@getControlPlaneACL',
]);

cy.url().should(
Expand Down
Loading