@@ -24,11 +24,15 @@ import {
2424 latestKubernetesVersion ,
2525 mockedLKEClusterTypes ,
2626 mockedLKEEnterprisePrices ,
27+ mockTieredEnterpriseVersions ,
28+ mockTieredStandardVersions ,
29+ mockTieredVersions ,
2730 nanodeNodeCount ,
2831 nanodeType ,
2932} from 'support/constants/lke' ;
3033import { mockGetAccount } from 'support/intercepts/account' ;
3134import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags' ;
35+ import { mockGetFirewalls } from 'support/intercepts/firewalls' ;
3236import { mockGetLinodeTypes } from 'support/intercepts/linodes' ;
3337import {
3438 mockCreateCluster ,
@@ -48,11 +52,13 @@ import {
4852 mockGetRegions ,
4953} from 'support/intercepts/regions' ;
5054import { ui } from 'support/ui' ;
55+ import { lkeClusterCreatePage } from 'support/ui/pages' ;
5156import { randomItem , randomLabel , randomNumber } from 'support/util/random' ;
5257import { chooseRegion , extendRegion } from 'support/util/regions' ;
5358
5459import {
5560 accountFactory ,
61+ firewallFactory ,
5662 kubeLinodeFactory ,
5763 kubernetesClusterFactory ,
5864 kubernetesControlPlaneACLFactory ,
@@ -64,6 +70,7 @@ import {
6470 CLUSTER_VERSIONS_DOCS_LINK ,
6571} from 'src/features/Kubernetes/constants' ;
6672import { getTotalClusterMemoryCPUAndStorage } from 'src/features/Kubernetes/kubeUtils' ;
73+ import { extendType } from 'src/utilities/extendType' ;
6774import { getTotalClusterPrice } from 'src/utilities/pricing/kubernetes' ;
6875
6976import type { PriceType } from '@linode/api-v4/lib/types' ;
@@ -1587,6 +1594,263 @@ describe('LKE Cluster Creation with LKE-E', () => {
15871594 } ) ;
15881595} ) ;
15891596
1597+ /*
1598+ * Tests for standard LKE create flow when the `lkeEnterprise.postLa` feature flag is enabled.
1599+ * The main change introduced by this feature flag is a new flow when adding node pools:
1600+ * Node pool size is specified inside of a configuration drawer instead of directly in the plan table,
1601+ * and additional node pool options have been added exclusively for LKE Enterprise clusters.
1602+ */
1603+ describe ( 'LKE cluster creation with LKE-E Post-LA' , ( ) => {
1604+ const mockRegions = [
1605+ ...regionFactory . buildList ( 3 , {
1606+ capabilities : [ 'Linodes' , 'Kubernetes' ] ,
1607+ } ) ,
1608+ ...regionFactory . buildList ( 3 , {
1609+ capabilities : [ 'Linodes' , 'Kubernetes' , 'Kubernetes Enterprise' ] ,
1610+ } ) ,
1611+ ] ;
1612+
1613+ const mockPlan = extendType ( linodeTypeFactory . build ( { class : 'standard' } ) ) ;
1614+ const mockPlans = [
1615+ mockPlan ,
1616+ ...linodeTypeFactory
1617+ . buildList ( 10 , { class : 'standard' } )
1618+ . map ( ( plan ) => extendType ( plan ) ) ,
1619+ ] ;
1620+
1621+ beforeEach ( ( ) => {
1622+ // TODO M3-8838: Remove feature flag `lkeEnterprise` mocks, remove redundant tests as-needed.
1623+ mockAppendFeatureFlags ( {
1624+ lkeEnterprise : {
1625+ enabled : true ,
1626+ la : true ,
1627+ postLa : true ,
1628+ phase2Mtc : false ,
1629+ } ,
1630+ } ) ;
1631+ mockGetAccount (
1632+ accountFactory . build ( {
1633+ capabilities : [ 'Kubernetes Enterprise' ] ,
1634+ } )
1635+ ) ;
1636+ mockGetKubernetesVersions ( mockTieredVersions . map ( ( version ) => version . id ) ) ;
1637+ mockGetTieredKubernetesVersions ( 'standard' , mockTieredStandardVersions ) ;
1638+ mockGetTieredKubernetesVersions ( 'enterprise' , mockTieredEnterpriseVersions ) ;
1639+ } ) ;
1640+
1641+ /*
1642+ * - Confirms that a user can create a standard LKE cluster when the LKE-E Post-LA feature is enabled.
1643+ * - Confirms that user can add and configure node pools via the Configure Node Pools drawer.
1644+ * - Confirms that LKE-E-specific node pool options are absent when configuring pools for standard clusters.
1645+ * - Confirms that outgoing cluster create API request contains the expected payload data.
1646+ * - Confirms that UI redirects to cluster details page upon successful cluster creation.
1647+ */
1648+ it ( 'can create standard LKE clusters with the LKE-E Post-LA feature enabled' , ( ) => {
1649+ const mockCluster = kubernetesClusterFactory . build ( {
1650+ label : randomLabel ( ) ,
1651+ tier : 'standard' ,
1652+ region : chooseRegion ( {
1653+ capabilities : [ 'Linodes' , 'Kubernetes' ] ,
1654+ regions : mockRegions ,
1655+ } ) . id ,
1656+ } ) ;
1657+
1658+ mockGetRegions ( mockRegions ) ;
1659+ mockGetLinodeTypes ( mockPlans ) ;
1660+ mockCreateCluster ( mockCluster ) . as ( 'createCluster' ) ;
1661+ mockGetCluster ( mockCluster ) ;
1662+
1663+ cy . visitWithLogin ( '/kubernetes/create' ) ;
1664+
1665+ // Configure a standard LKE cluster.
1666+ lkeClusterCreatePage . setLabel ( mockCluster . label ) ;
1667+ lkeClusterCreatePage . selectClusterTier ( 'standard' ) ;
1668+ lkeClusterCreatePage . selectRegionById ( mockCluster . region , mockRegions ) ;
1669+ lkeClusterCreatePage . setEnableApl ( false ) ;
1670+ lkeClusterCreatePage . setEnableHighAvailability ( false ) ;
1671+
1672+ // Configure a node pool with the default pool size of 3.
1673+ // Additionally assert that LKE-E specific options are absent in the drawer,
1674+ // and that the order summary section updates to reflect the user's selection.
1675+ lkeClusterCreatePage . selectPlanTab ( 'Shared CPU' ) ;
1676+ lkeClusterCreatePage . selectNodePoolPlan ( mockPlan . formattedLabel ) ;
1677+ lkeClusterCreatePage . withinNodePoolDrawer ( mockPlan . formattedLabel , ( ) => {
1678+ cy . findByLabelText ( 'Update Strategy' ) . should ( 'not.exist' ) ;
1679+ cy . findByLabelText ( 'Firewall' ) . should ( 'not.exist' ) ;
1680+
1681+ ui . button
1682+ . findByTitle ( 'Add Pool' )
1683+ . should ( 'be.visible' )
1684+ . should ( 'be.enabled' )
1685+ . click ( ) ;
1686+ } ) ;
1687+
1688+ lkeClusterCreatePage . withinOrderSummary ( ( ) => {
1689+ cy . contains ( mockPlan . formattedLabel )
1690+ . closest ( '[data-testid="node-pool-summary"]' )
1691+ . within ( ( ) => {
1692+ cy . findByText ( '3 Nodes' ) . should ( 'be.visible' ) ;
1693+ cy . findByText ( 'Edit Configuration' ) . should ( 'be.visible' ) . click ( ) ;
1694+ } ) ;
1695+ } ) ;
1696+
1697+ // Confirm that node pool size can be configured, and that the order summary
1698+ // UI updates upon clicking the "Update Pool" button.
1699+ lkeClusterCreatePage . withinNodePoolDrawer ( mockPlan . formattedLabel , ( ) => {
1700+ cy . findByLabelText ( 'Add 1' )
1701+ . should ( 'be.visible' )
1702+ . should ( 'be.enabled' )
1703+ . click ( ) ;
1704+
1705+ ui . button
1706+ . findByTitle ( 'Update Pool' )
1707+ . should ( 'be.visible' )
1708+ . should ( 'be.enabled' )
1709+ . click ( ) ;
1710+ } ) ;
1711+
1712+ lkeClusterCreatePage . withinOrderSummary ( ( ) => {
1713+ cy . contains ( mockPlan . formattedLabel )
1714+ . closest ( '[data-testid="node-pool-summary"]' )
1715+ . within ( ( ) => {
1716+ cy . findByText ( '4 Nodes' ) . should ( 'be.visible' ) ;
1717+ } ) ;
1718+
1719+ // Create the LKE cluster and confirm that the outgoing API request contains
1720+ // the expected payload data.
1721+ ui . button
1722+ . findByTitle ( 'Create Cluster' )
1723+ . should ( 'be.visible' )
1724+ . should ( 'be.enabled' )
1725+ . click ( ) ;
1726+ } ) ;
1727+
1728+ cy . wait ( '@createCluster' ) . then ( ( xhr ) => {
1729+ const body = xhr . request . body ;
1730+
1731+ // Validate outgoing `node_pools` request payload configuration.
1732+ expect ( body [ 'node_pools' ] ) . to . be . an ( 'array' ) ;
1733+ expect ( body [ 'node_pools' ] ) . to . have . length ( 1 ) ;
1734+ expect ( body [ 'node_pools' ] [ 0 ] ! . type ) . to . equal ( mockPlan . id ) ;
1735+ expect ( body [ 'node_pools' ] [ 0 ] ! . count ) . to . equal ( 4 ) ;
1736+
1737+ // Validate that the rest of the payload matches the user's input.
1738+ expect ( body [ 'region' ] ) . to . equal ( mockCluster . region ) ;
1739+ expect ( body [ 'label' ] ) . to . equal ( mockCluster . label ) ;
1740+ expect ( body [ 'tier' ] ) . to . equal ( 'standard' ) ;
1741+ expect ( body [ 'apl_enabled' ] ) . to . be . false ;
1742+ expect ( body [ 'control_plane' ] [ 'acl' ] [ 'enabled' ] ) . to . be . false ;
1743+ expect ( body [ 'control_plane' ] [ 'high_availability' ] ) . to . be . false ;
1744+ } ) ;
1745+
1746+ cy . url ( ) . should (
1747+ 'endWith' ,
1748+ `/kubernetes/clusters/${ mockCluster . id } /summary`
1749+ ) ;
1750+ } ) ;
1751+
1752+ /*
1753+ * - Confirms that regular LKE cluster creation works when a user initially configures an LKE-E cluster.
1754+ * - Configures an LKE-E cluster with LKE-E specific choices, then switches to a regular cluster before proceeding.
1755+ * - Confirms that outgoing cluster request respects user selection, cluster is created as expected.
1756+ */
1757+ it ( 'can switch to a standard cluster after configuring an LKE-E cluster with LKE-E Post-LA feature enabled' , ( ) => {
1758+ const mockCluster = kubernetesClusterFactory . build ( {
1759+ label : randomLabel ( ) ,
1760+ tier : 'standard' ,
1761+ region : chooseRegion ( {
1762+ capabilities : [ 'Linodes' , 'Kubernetes' , 'Kubernetes Enterprise' ] ,
1763+ regions : mockRegions ,
1764+ } ) . id ,
1765+ } ) ;
1766+
1767+ const mockFirewall = firewallFactory . build ( ) ;
1768+
1769+ mockGetRegions ( mockRegions ) ;
1770+ mockGetFirewalls ( [ mockFirewall ] ) ;
1771+ mockGetLinodeTypes ( mockPlans ) ;
1772+ mockCreateCluster ( mockCluster ) . as ( 'createCluster' ) ;
1773+ mockGetCluster ( mockCluster ) ;
1774+
1775+ cy . visitWithLogin ( '/kubernetes/create' ) ;
1776+
1777+ lkeClusterCreatePage . setLabel ( mockCluster . label ) ;
1778+ lkeClusterCreatePage . selectClusterTier ( 'enterprise' ) ;
1779+ lkeClusterCreatePage . selectRegionById ( mockCluster . region , mockRegions ) ;
1780+ lkeClusterCreatePage . selectPlanTab ( 'Shared CPU' ) ;
1781+
1782+ // Confirm that order summary updates to reflect that Enterprise tier is selected,
1783+ // then configure a node pool.
1784+ lkeClusterCreatePage . withinOrderSummary ( ( ) => {
1785+ cy . findByText ( 'LKE Enterprise' ) . should ( 'be.visible' ) ;
1786+ } ) ;
1787+
1788+ lkeClusterCreatePage . selectNodePoolPlan ( mockPlan . formattedLabel ) ;
1789+ lkeClusterCreatePage . withinNodePoolDrawer ( mockPlan . formattedLabel , ( ) => {
1790+ // Confirm that LKE-E specific options are present.
1791+
1792+ // Set "Update Strategy" to "Rolling Updates".
1793+ cy . findByText ( 'Update Strategy' ) . should ( 'be.visible' ) . click ( ) ;
1794+ cy . focused ( ) . type ( 'Rolling Updates' ) ;
1795+ ui . autocompletePopper . findByTitle ( 'Rolling Updates' ) . click ( ) ;
1796+
1797+ // Select the existing mock firewall.
1798+ cy . findByText ( 'Select existing firewall' ) . click ( ) ;
1799+ cy . get ( '[data-qa-autocomplete="Firewall"]' ) . within ( ( ) => {
1800+ cy . findByLabelText ( 'Firewall' ) . type ( mockFirewall . label ) ;
1801+ ui . autocompletePopper . findByTitle ( mockFirewall . label ) . click ( ) ;
1802+ } ) ;
1803+
1804+ ui . button
1805+ . findByTitle ( 'Add Pool' )
1806+ . should ( 'be.visible' )
1807+ . should ( 'be.enabled' )
1808+ . click ( ) ;
1809+ } ) ;
1810+
1811+ // Now switch to a standard LKE cluster, assert the state of the UI and
1812+ // outgoing API request after the user makes this switch.
1813+ lkeClusterCreatePage . selectClusterTier ( 'standard' ) ;
1814+ lkeClusterCreatePage . setEnableApl ( false ) ;
1815+ lkeClusterCreatePage . setEnableHighAvailability ( true ) ;
1816+
1817+ lkeClusterCreatePage . withinOrderSummary ( ( ) => {
1818+ cy . findByText ( 'LKE Enterprise' ) . should ( 'not.exist' ) ;
1819+
1820+ // Create the LKE cluster and assert that the outgoing API request contains
1821+ // the expected payload data.
1822+ ui . button
1823+ . findByTitle ( 'Create Cluster' )
1824+ . should ( 'be.visible' )
1825+ . should ( 'be.enabled' )
1826+ . click ( ) ;
1827+ } ) ;
1828+
1829+ cy . wait ( '@createCluster' ) . then ( ( xhr ) => {
1830+ const body = xhr . request . body ;
1831+ expect ( body [ 'label' ] ) . to . equal ( mockCluster . label ) ;
1832+ expect ( body [ 'region' ] ) . to . equal ( mockCluster . region ) ;
1833+ expect ( body [ 'tier' ] ) . to . equal ( 'standard' ) ;
1834+ expect ( body [ 'control_plane' ] [ 'acl' ] [ 'enabled' ] ) . to . be . false ;
1835+ expect ( body [ 'control_plane' ] [ 'high_availability' ] ) . to . be . true ;
1836+ expect ( body [ 'apl_enabled' ] ) . to . be . false ;
1837+
1838+ const nodePools = body [ 'node_pools' ] ;
1839+ expect ( nodePools ) . to . be . an ( 'array' ) ;
1840+ expect ( nodePools ) . to . have . length ( 1 ) ;
1841+ expect ( nodePools [ 0 ] ) . to . be . an ( 'object' ) ;
1842+ expect ( nodePools [ 0 ] [ 'type' ] ) . to . equal ( mockPlan . id ) ;
1843+ expect ( nodePools [ 0 ] [ 'count' ] ) . to . equal ( 3 ) ;
1844+
1845+ // TODO M3-10590 - Uncomment and adjust according to chosen resolution.
1846+ // expect(nodePools[0]['update_strategy']).to.be.undefined;
1847+ // expect(nodePools[0]['firewall_id']).to.be.undefined;
1848+ } ) ;
1849+
1850+ cy . url ( ) . should ( 'endWith' , `kubernetes/clusters/${ mockCluster . id } /summary` ) ;
1851+ } ) ;
1852+ } ) ;
1853+
15901854/**
15911855 * Returns each plan in an array which is similar to the given plan.
15921856 *
0 commit comments