Skip to content

Commit 7e96024

Browse files
committed
add scale pools tests
1 parent 815df33 commit 7e96024

File tree

4 files changed

+190
-51
lines changed

4 files changed

+190
-51
lines changed

cypress/e2e/po/detail/provisioning.cattle.io.cluster/cluster-detail.po.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import PagePo from '@/cypress/e2e/po/pages/page.po';
1+
import { BaseDetailPagePo } from '@/cypress/e2e/po/pages/base/base-detail-page.po';
22
import MachinePoolsListPo from '@/cypress/e2e/po/lists/machine-pools-list.po';
33
import ClusterConditionsListPo from '~/cypress/e2e/po/lists/cluster-conditions-list.po';
44
import ClusterProvisioningLogPo from '~/cypress/e2e/po/lists/cluster-provisioning-log.po';
@@ -7,10 +7,11 @@ import ClusterSnapshotsListPo from '~/cypress/e2e/po/lists/cluster-snapshots-lis
77
import TabbedPo from '~/cypress/e2e/po/components/tabbed.po';
88
import ClusterRecentEventsListPo from '~/cypress/e2e/po/lists/cluster-recent-events-list.po';
99
import DetailDrawer from '@/cypress/e2e/po/side-bars/detail-drawer.po';
10+
1011
/**
1112
* Covers core functionality that's common to the dashboard's cluster detail pages
1213
*/
13-
export default abstract class ClusterManagerDetailPagePo extends PagePo {
14+
export default abstract class ClusterManagerDetailPagePo extends BaseDetailPagePo {
1415
private static createPath(clusterId: string, clusterName: string, tab?: string) {
1516
const namespace = clusterName === 'local' ? 'fleet-local' : 'fleet-default';
1617

@@ -46,7 +47,7 @@ export default abstract class ClusterManagerDetailPagePo extends PagePo {
4647
}
4748

4849
machinePoolsList() {
49-
return new MachinePoolsListPo(this.self().find('[data-testid="sortable-table-list-container"]'));
50+
return new MachinePoolsListPo(this.self().find('#machine-pools [data-testid="sortable-table-list-container"]'));
5051
}
5152

5253
conditionsList() {

cypress/e2e/po/lists/machine-pools-list.po.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import BaseResourceList from '@/cypress/e2e/po/lists/base-resource-list.po';
22
import ResourceTablePo from '@/cypress/e2e/po/components/resource-table.po';
3+
import TooltipPo from '@/cypress/e2e/po/components/tooltip.po';
4+
import { GetOptions } from '@/cypress/e2e/po/components/component.po';
35

46
export default class MachinePoolsListPo extends BaseResourceList {
57
details(name: string, index: number) {
@@ -9,4 +11,27 @@ export default class MachinePoolsListPo extends BaseResourceList {
911
downloadYamlButton() {
1012
return new ResourceTablePo(this.self()).downloadYamlButton();
1113
}
14+
15+
machinePoolCount(poolName: string, count: any, options?: GetOptions) {
16+
return this.resourceTable().sortableTable().groupElementWithName(poolName)
17+
.find('.group-header-buttons')
18+
.contains(count, options);
19+
}
20+
21+
scaleDownButton(poolName: string) {
22+
return this.resourceTable().sortableTable().groupElementWithName(poolName)
23+
.find('.group-header-buttons button')
24+
.first();
25+
}
26+
27+
scaleUpButton(poolName: string) {
28+
return this.resourceTable().sortableTable().groupElementWithName(poolName)
29+
.find('.group-header-buttons button')
30+
.last();
31+
}
32+
33+
scaleButtonTooltip(poolName: string, button: 'plus' | 'minus'): TooltipPo {
34+
return new TooltipPo(this.resourceTable().sortableTable().groupElementWithName(poolName)
35+
.find(`.group-header-buttons button .icon-${ button }`));
36+
}
1237
}

cypress/e2e/po/prompts/genericPrompt.po.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ComponentPo from '@/cypress/e2e/po/components/component.po';
22
import CardPo from '@/cypress/e2e/po/components/card.po';
33
import LabeledSelectPo from '@/cypress/e2e/po/components/labeled-select.po';
4+
import CheckboxInputPo from '@/cypress/e2e/po/components/checkbox-input.po';
45

56
export default class GenericPrompt extends ComponentPo {
67
card = new CardPo();
@@ -17,6 +18,10 @@ export default class GenericPrompt extends ComponentPo {
1718
return new LabeledSelectPo(selector);
1819
}
1920

21+
checkbox(selector = '[data-checkbox-ctrl]') {
22+
return new CheckboxInputPo(this.self().get(selector));
23+
}
24+
2025
clickActionButton(text: string) {
2126
return this.card.getActionButton().contains(text).click();
2227
}

cypress/e2e/tests/pages/manager/cluster-provisioning-amazon-ec2-rke2.spec.ts

Lines changed: 156 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ import ClusterManagerEditGenericPagePo from '@/cypress/e2e/po/edit/provisioning.
66
import PromptRemove from '@/cypress/e2e/po/prompts/promptRemove.po';
77
import LoadingPo from '@/cypress/e2e/po/components/loading.po';
88
import TabbedPo from '@/cypress/e2e/po/components/tabbed.po';
9-
import { MEDIUM_TIMEOUT_OPT } from '@/cypress/support/utils/timeouts';
9+
import { LONG_TIMEOUT_OPT, MEDIUM_TIMEOUT_OPT } from '@/cypress/support/utils/timeouts';
1010
import { USERS_BASE_URL } from '@/cypress/support/utils/api-endpoints';
11+
import { promptModal } from '@/cypress/e2e/po/prompts/shared/modalInstances.po';
1112

1213
// will only run this in jenkins pipeline where cloud credentials are stored
13-
describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation: 'off', tags: ['@manager', '@adminUser', '@standardUser', '@jenkins'] }, () => {
14+
describe('Deploy RKE2 cluster using node driver on Amazon EC2', { tags: ['@manager', '@adminUser', '@standardUser', '@jenkins'] }, () => {
1415
const clusterList = new ClusterManagerListPagePo();
1516
const loadingPo = new LoadingPo('.loading-indicator');
1617

1718
let removeCloudCred = false;
1819
let cloudcredentialId = '';
19-
let k8sVersion = '';
20+
let latestK8sVersion = '';
2021
let olderK8sVersion = '';
2122
let clusterId = '';
2223

2324
before(() => {
2425
cy.login();
25-
HomePagePo.goTo();
2626

2727
// clean up amazon cloud credentials
2828
cy.getRancherResource('v3', 'cloudcredentials', null, null).then((resp: Cypress.Response<any>) => {
@@ -43,6 +43,8 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
4343
});
4444

4545
beforeEach(() => {
46+
cy.login();
47+
HomePagePo.goTo();
4648
cy.createE2EResourceName('rke2ec2cluster').as('rke2Ec2ClusterName');
4749
cy.createE2EResourceName('ec2cloudcredential').as('ec2CloudCredentialName');
4850
});
@@ -85,47 +87,48 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
8587
createRKE2ClusterPage.nameNsDescription().name().set(this.rke2Ec2ClusterName);
8688
createRKE2ClusterPage.nameNsDescription().description().set(`${ this.rke2Ec2ClusterName }-description`);
8789

88-
// Get kubernetes versions - use an older version for initial creation, then upgrade later
89-
cy.wait('@getRke2Releases').then(({ response }) => {
90-
expect(response.statusCode).to.eq(200);
91-
const index1 = response.body.data.length - 1;
92-
93-
// Store the latest version for upgrade test
94-
k8sVersion = response.body.data[index1].id;
95-
cy.wrap(k8sVersion).as('k8sVersion');
90+
// Get kubernetes versions from the UI dropdown
91+
// Index 0 is the "RKE2" header, so actual versions start at index 1
92+
cy.wait('@getRke2Releases').its('response.statusCode').should('eq', 200);
93+
createRKE2ClusterPage.basicsTab().kubernetesVersions().toggle();
94+
createRKE2ClusterPage.basicsTab().kubernetesVersions().getOptions().then(($options) => {
95+
// First RKE2 version (index 1) is the latest version
96+
latestK8sVersion = Cypress.$($options[1]).text().trim();
97+
cy.wrap(latestK8sVersion).as('latestK8sVersion');
98+
99+
// Second RKE2 version (index 2) is the older version for cluster creation
100+
olderK8sVersion = Cypress.$($options[2]).text().trim();
101+
cy.wrap(olderK8sVersion).as('olderK8sVersion');
96102

97-
// Use the older version for cluster creation
98-
// This allows us to test the upgrade scenario
99-
const index2 = index1 - 1;
103+
cy.log(`latestK8sVersion: ${ latestK8sVersion }`);
104+
cy.log(`olderK8sVersion: ${ olderK8sVersion }`);
100105

101-
olderK8sVersion = response.body.data[index2].id;
102-
cy.wrap(olderK8sVersion).as('olderK8sVersion');
106+
// Set the k8s version
107+
createRKE2ClusterPage.basicsTab().kubernetesVersions().clickOptionWithLabel(olderK8sVersion);
103108
});
104109

105-
cy.get<string>('@olderK8sVersion').then((version) => {
106-
createRKE2ClusterPage.basicsTab().kubernetesVersions().toggle();
107-
createRKE2ClusterPage.basicsTab().kubernetesVersions().clickOptionWithLabel(version);
108-
109-
createRKE2ClusterPage.machinePoolTab().networks().toggle();
110-
createRKE2ClusterPage.machinePoolTab().networks().clickOptionWithLabel('default');
111-
112-
cy.intercept('POST', 'v1/provisioning.cattle.io.clusters').as('createRke2Cluster');
113-
createRKE2ClusterPage.create();
114-
cy.wait('@createRke2Cluster').then(({ response }) => {
115-
expect(response?.statusCode).to.eq(201);
116-
expect(response?.body).to.have.property('kind', 'Cluster');
117-
expect(response?.body.metadata).to.have.property('name', this.rke2Ec2ClusterName);
118-
expect(response?.body.spec).to.have.property('kubernetesVersion').contains(version);
119-
clusterId = response?.body.id;
120-
});
121-
clusterList.waitForPage();
122-
clusterList.list().state(this.rke2Ec2ClusterName).should('be.visible')
123-
.and(($el) => {
124-
const status = $el.text().trim();
110+
// Set the network
111+
createRKE2ClusterPage.machinePoolTab().networks().toggle();
112+
createRKE2ClusterPage.machinePoolTab().networks().clickOptionWithLabel('default');
125113

126-
expect(['Reconciling', 'Updating']).to.include(status);
127-
});
114+
// Create the cluster
115+
cy.intercept('POST', 'v1/provisioning.cattle.io.clusters').as('createRke2Cluster');
116+
createRKE2ClusterPage.create();
117+
cy.wait('@createRke2Cluster').then(({ response }) => {
118+
expect(response?.statusCode).to.eq(201);
119+
expect(response?.body).to.have.property('kind', 'Cluster');
120+
expect(response?.body.metadata).to.have.property('name', this.rke2Ec2ClusterName);
121+
expect(response?.body.spec).to.have.property('kubernetesVersion').contains(olderK8sVersion);
122+
clusterId = response?.body.id;
128123
});
124+
125+
clusterList.waitForPage();
126+
clusterList.list().state(this.rke2Ec2ClusterName).should('be.visible')
127+
.and(($el) => {
128+
const status = $el.text().trim();
129+
130+
expect(['Reconciling', 'Updating']).to.include(status);
131+
});
129132
});
130133

131134
it('can see details of cluster in cluster list', function() {
@@ -164,13 +167,8 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
164167
clusterList.list().name(this.rke2Ec2ClusterName).click();
165168
clusterDetails.waitForPage(null, 'machine-pools');
166169
clusterDetails.resourceDetail().title().should('contain', this.rke2Ec2ClusterName);
167-
clusterDetails.machinePoolsList().details(`${ this.rke2Ec2ClusterName }-pool1-`, 1).should('contain', 'Running');
168170

169171
// check cluster details page > recent events
170-
ClusterManagerListPagePo.navTo();
171-
clusterList.waitForPage();
172-
clusterList.goToDetailsPage(this.rke2Ec2ClusterName, '.cluster-link a');
173-
clusterDetails.waitForPage(null, 'machine-pools');
174172
clusterDetails.selectTab(tabbedPo, '[data-testid="btn-events"]');
175173
clusterDetails.waitForPage(null, 'events');
176174
clusterDetails.recentEventsList().checkTableIsEmpty();
@@ -192,7 +190,7 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
192190

193191
// Select the latest version for upgrade
194192
editClusterPage.basicsTab().kubernetesVersions().toggle();
195-
editClusterPage.basicsTab().kubernetesVersions().clickOptionWithLabel(k8sVersion);
193+
editClusterPage.basicsTab().kubernetesVersions().clickOptionWithLabel(latestK8sVersion);
196194

197195
cy.intercept('PUT', `/v1/provisioning.cattle.io.clusters/fleet-default/${ this.rke2Ec2ClusterName }`).as('clusterUpdate');
198196
// Save the cluster to upgrade the Kubernetes version
@@ -212,7 +210,7 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
212210
expect(response?.statusCode).to.equal(200);
213211
expect(response?.body).to.have.property('kind', 'Cluster');
214212
expect(response?.body.metadata).to.have.property('name', this.rke2Ec2ClusterName);
215-
expect(response?.body.spec).to.have.property('kubernetesVersion').contains(k8sVersion);
213+
expect(response?.body.spec).to.have.property('kubernetesVersion').contains(latestK8sVersion);
216214
});
217215

218216
clusterList.waitForPage();
@@ -221,15 +219,15 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
221219

222220
// check k8s version
223221
clusterList.list().version(this.rke2Ec2ClusterName).then((el) => {
224-
expect(el.text().trim()).contains(k8sVersion);
222+
expect(el.text().trim()).contains(latestK8sVersion);
225223
});
226224

227225
// Navigate back to edit page to verify older version is disabled in dropdown
228226
clusterList.list().actionMenu(this.rke2Ec2ClusterName).getMenuItem('Edit Config').click();
229227
editClusterPage.waitForPage('mode=edit', 'basic');
230228

231229
// Verify current version is selected
232-
editClusterPage.basicsTab().kubernetesVersions().checkContainsOptionSelected(k8sVersion);
230+
editClusterPage.basicsTab().kubernetesVersions().checkContainsOptionSelected(latestK8sVersion);
233231

234232
// Open dropdown and verify older version is disabled
235233
editClusterPage.basicsTab().kubernetesVersions().toggle();
@@ -268,6 +266,116 @@ describe('Deploy RKE2 cluster using node driver on Amazon EC2', { testIsolation:
268266
clusterDetails.snapshotsList().checkSnapshotExist(`on-demand-${ this.rke2Ec2ClusterName }`);
269267
});
270268

269+
it('can scale up a machine pool', function() {
270+
// testing https://github.com/rancher/dashboard/issues/13285
271+
const clusterDetails = new ClusterManagerDetailRke2AmazonEc2PagePo(undefined, this.rke2Ec2ClusterName);
272+
273+
ClusterManagerListPagePo.navTo();
274+
clusterList.waitForPage();
275+
276+
// Navigate to cluster details page > machine pools
277+
clusterList.list().name(this.rke2Ec2ClusterName).click();
278+
clusterDetails.waitForPage(null, 'machine-pools');
279+
clusterDetails.resourceDetail().title().should('contain', this.rke2Ec2ClusterName);
280+
281+
// Verify scaling buttons are present in the machine pools section
282+
clusterDetails.machinePoolsList().resourceTable().sortableTable().groupByButtons(1)
283+
.click();
284+
285+
// Check for scale up button (it should be enabled)
286+
clusterDetails.machinePoolsList().scaleUpButton(`${ this.rke2Ec2ClusterName }-pool1`)
287+
.should('be.visible')
288+
.and('be.enabled');
289+
290+
// Hover scale up button - tooltip should read "Scale Pool Up"
291+
clusterDetails.machinePoolsList().scaleButtonTooltip(`${ this.rke2Ec2ClusterName }-pool1`, 'plus')
292+
.waitForTooltipWithText('Scale Pool Up');
293+
clusterDetails.machinePoolsList().scaleButtonTooltip(`${ this.rke2Ec2ClusterName }-pool1`, 'plus')
294+
.hideTooltip();
295+
296+
// Check for scale down button (it should be disabled initially)
297+
clusterDetails.machinePoolsList().scaleDownButton(`${ this.rke2Ec2ClusterName }-pool1`)
298+
.should('be.visible')
299+
.and('be.disabled');
300+
301+
// Hover scale down button - tooltip should read "Scale Pool Down"
302+
clusterDetails.machinePoolsList().scaleButtonTooltip(`${ this.rke2Ec2ClusterName }-pool1`, 'minus')
303+
.waitForTooltipWithText('Scale Pool Down');
304+
clusterDetails.machinePoolsList().scaleButtonTooltip(`${ this.rke2Ec2ClusterName }-pool1`, 'minus')
305+
.hideTooltip();
306+
307+
cy.intercept('PUT', ` /v1/provisioning.cattle.io.clusters/fleet-default/${ this.rke2Ec2ClusterName }`).as('scaleUpMachineDeployment');
308+
// Scale up the machine pool
309+
clusterDetails.machinePoolsList().scaleUpButton(`${ this.rke2Ec2ClusterName }-pool1`)
310+
.click();
311+
312+
cy.wait('@scaleUpMachineDeployment').its('response.statusCode').should('eq', 200);
313+
314+
// Verify the machine pool is scaled up to 2
315+
clusterDetails.machinePoolsList().machinePoolCount(`${ this.rke2Ec2ClusterName }-pool1`, /^2$/, { timeout: 700000 });
316+
clusterDetails.machinePoolsList().resourceTable().sortableTable().checkRowCount(false, 2, LONG_TIMEOUT_OPT);
317+
318+
// Verify the scale down button is now enabled (since we have 2 nodes)
319+
clusterDetails.machinePoolsList().scaleDownButton(`${ this.rke2Ec2ClusterName }-pool1`)
320+
.should('be.enabled');
321+
322+
// Verify the cluster is active
323+
clusterDetails.resourceDetail().masthead().resourceStatus().contains('Active', { timeout: 700000 });
324+
clusterDetails.machinePoolsList().resourceTable().sortableTable().checkRowCount(false, 2, MEDIUM_TIMEOUT_OPT);
325+
});
326+
327+
it('can scale down a machine pool', function() {
328+
// testing https://github.com/rancher/dashboard/issues/13285
329+
// Set user preference to ensure the scale down confirmation modal always appears
330+
cy.setUserPreference({ 'scale-pool-prompt': false });
331+
332+
const clusterDetails = new ClusterManagerDetailRke2AmazonEc2PagePo(undefined, this.rke2Ec2ClusterName);
333+
334+
ClusterManagerListPagePo.navTo();
335+
clusterList.waitForPage();
336+
337+
// Navigate to cluster details page > machine pools
338+
clusterList.list().name(this.rke2Ec2ClusterName).click();
339+
clusterDetails.waitForPage(null, 'machine-pools');
340+
clusterDetails.resourceDetail().title().should('contain', this.rke2Ec2ClusterName);
341+
342+
// Verify we have 2 nodes to start with (from the previous scale up test)
343+
clusterDetails.machinePoolsList().resourceTable().sortableTable().groupByButtons(1)
344+
.click();
345+
346+
clusterDetails.machinePoolsList().machinePoolCount(`${ this.rke2Ec2ClusterName }-pool1`, 2, MEDIUM_TIMEOUT_OPT);
347+
348+
// Verify the scale down button is enabled
349+
clusterDetails.machinePoolsList().scaleDownButton(`${ this.rke2Ec2ClusterName }-pool1`)
350+
.should('be.visible')
351+
.and('be.enabled');
352+
353+
cy.intercept('PUT', `/v1/provisioning.cattle.io.clusters/fleet-default/${ this.rke2Ec2ClusterName }`).as('scaleDownMachineDeployment');
354+
355+
// Scale down the machine pool
356+
clusterDetails.machinePoolsList().scaleDownButton(`${ this.rke2Ec2ClusterName }-pool1`)
357+
.click();
358+
359+
// Handle the scale down confirmation dialog
360+
promptModal().getBody().should('contain', 'You are attempting to delete the MachineDeployment');
361+
promptModal().getBody().should('contain', `${ this.rke2Ec2ClusterName }-pool1`);
362+
promptModal().clickActionButton('Confirm');
363+
364+
cy.wait('@scaleDownMachineDeployment').its('response.statusCode').should('eq', 200);
365+
366+
// Verify the machine pool is scaled down to 1
367+
clusterDetails.machinePoolsList().machinePoolCount(`${ this.rke2Ec2ClusterName }-pool1`, /^1$/, MEDIUM_TIMEOUT_OPT);
368+
369+
// Verify the cluster is updating -> active
370+
clusterDetails.resourceDetail().masthead().resourceStatus().contains('Updating');
371+
clusterDetails.resourceDetail().masthead().resourceStatus().contains('Active', { timeout: 700000 });
372+
clusterDetails.machinePoolsList().resourceTable().sortableTable().checkRowCount(false, 1, { timeout: 700000 });
373+
374+
// Verify the scale down button is now disabled (can't scale below 1)
375+
clusterDetails.machinePoolsList().scaleDownButton(`${ this.rke2Ec2ClusterName }-pool1`)
376+
.should('be.disabled');
377+
});
378+
271379
it('can delete an Amazon EC2 RKE2 cluster', function() {
272380
ClusterManagerListPagePo.navTo();
273381
clusterList.waitForPage();

0 commit comments

Comments
 (0)