Skip to content

Commit

Permalink
Merge pull request #10180 from linode/staging
Browse files Browse the repository at this point in the history
Release v1.112.0 - staging → master
  • Loading branch information
cpathipa authored Feb 13, 2024
2 parents 1f5650a + a9d02f1 commit f1d10aa
Show file tree
Hide file tree
Showing 52 changed files with 2,007 additions and 348 deletions.
21 changes: 21 additions & 0 deletions packages/manager/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [2024-02-13] - v1.112.0

### Added:

- Support for IPv4 Ranges in VPC 'Assign Linodes to subnet' drawer ([#10089](https://github.com/linode/manager/pull/10089))
- VPC IPv4 address and range to Linode IP Address Table ([#10108](https://github.com/linode/manager/pull/10108))
- Support for VPC IPv4 Ranges data in Unassign Linodes drawer ([#10114](https://github.com/linode/manager/pull/10114))
- Support for VPC IPv4 Ranges in Linode Create flow and 'VPC IPv4 Ranges' column to inner Subnets table on VPC Detail page ([#10116](https://github.com/linode/manager/pull/10116))
- Support VPC IPv4 Ranges in Add/Edit Linode Config dialog ([#10170](https://github.com/linode/manager/pull/10170))

### Changed:

- "Learn more" docs link for IPv4 ranges in Add/Edit Linode Config dialog, Linode Create flow, and VPC "Assign Linodes" drawer

### Fixed:

- Error when enabling backups for Linodes in regions with $0 pricing ([#10153](https://github.com/linode/manager/pull/10153))
- Error notices for $0 regions in LKE Resize and Add Node Pools drawers ([#10157](https://github.com/linode/manager/pull/10157))
- Error in Enable All Backups drawer when one or more Linode is in a $0 region ([#10161](https://github.com/linode/manager/pull/10161))
- Display $0.00 prices in Linode Migration dialog ([#10166](https://github.com/linode/manager/pull/10166))

## [2024-02-05] - v1.111.0
### Changed:

Expand Down
225 changes: 225 additions & 0 deletions packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1011,4 +1011,229 @@ describe('LKE cluster updates for DC-specific prices', () => {
// Confirm total price updates in Kube Specs: $14.40/mo existing pool + $28.80/mo new pool.
cy.findByText('$43.20/month').should('be.visible');
});

/*
* - Confirms node pool resize UI flow using mocked API responses.
* - Confirms that pool size can be changed.
* - Confirms that drawer reflects $0 pricing.
* - Confirms that details page still shows $0 pricing after resizing.
*/
it('can resize pools with region prices of $0', () => {
const dcSpecificPricingRegion = getRegionById('us-southeast');

const mockCluster = kubernetesClusterFactory.build({
k8s_version: latestKubernetesVersion,
region: dcSpecificPricingRegion.id,
control_plane: {
high_availability: false,
},
});

const mockNodePoolResized = nodePoolFactory.build({
count: 3,
type: dcPricingMockLinodeTypes[2].id,
nodes: kubeLinodeFactory.buildList(3),
});

const mockNodePoolInitial = {
...mockNodePoolResized,
count: 1,
nodes: [mockNodePoolResized.nodes[0]],
};

const mockLinodes: Linode[] = mockNodePoolResized.nodes.map(
(node: PoolNodeResponse): Linode => {
return linodeFactory.build({
id: node.instance_id ?? undefined,
ipv4: [randomIp()],
region: dcSpecificPricingRegion.id,
type: dcPricingMockLinodeTypes[2].id,
});
}
);

const mockNodePoolDrawerTitle = 'Resize Pool: Linode 2 GB Plan';

mockGetCluster(mockCluster).as('getCluster');
mockGetClusterPools(mockCluster.id, [mockNodePoolInitial]).as(
'getNodePools'
);
mockGetLinodes(mockLinodes).as('getLinodes');
mockGetLinodeType(dcPricingMockLinodeTypes[2]).as('getLinodeType');
mockGetKubernetesVersions().as('getVersions');
mockGetDashboardUrl(mockCluster.id);
mockGetApiEndpoints(mockCluster.id);

cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}`);
cy.wait([
'@getCluster',
'@getNodePools',
'@getLinodes',
'@getVersions',
'@getLinodeType',
]);

// Confirm that nodes are visible.
mockNodePoolInitial.nodes.forEach((node: PoolNodeResponse) => {
cy.get(`tr[data-qa-node-row="${node.id}"]`)
.should('be.visible')
.within(() => {
const nodeLinode = mockLinodes.find(
(linode: Linode) => linode.id === node.instance_id
);
if (nodeLinode) {
cy.findByText(nodeLinode.label).should('be.visible');
}
});
});

// Confirm total price is listed in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');

// Click "Resize Pool" and increase size to 4 nodes.
ui.button
.findByTitle('Resize Pool')
.should('be.visible')
.should('be.enabled')
.click();

mockUpdateNodePool(mockCluster.id, mockNodePoolResized).as(
'resizeNodePool'
);
mockGetClusterPools(mockCluster.id, [mockNodePoolResized]).as(
'getNodePools'
);

ui.drawer
.findByTitle(mockNodePoolDrawerTitle)
.should('be.visible')
.within(() => {
ui.button
.findByTitle('Save Changes')
.should('be.visible')
.should('be.disabled');

cy.findByText('Current pool: $0/month (1 node at $0/month)').should(
'be.visible'
);
cy.findByText('Resized pool: $0/month (1 node at $0/month)').should(
'be.visible'
);

cy.findByLabelText('Add 1')
.should('be.visible')
.should('be.enabled')
.click()
.click()
.click();

cy.findByLabelText('Edit Quantity').should('have.value', '4');
cy.findByText('Current pool: $0/month (1 node at $0/month)').should(
'be.visible'
);
cy.findByText('Resized pool: $0/month (4 nodes at $0/month)').should(
'be.visible'
);

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

cy.wait(['@resizeNodePool', '@getNodePools']);

// Confirm total price is still $0 in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');
});

/*
* - Confirms UI flow when adding node pools using mocked API responses.
* - Confirms that drawer reflects $0 prices.
* - Confirms that details page still shows $0 pricing after adding node pool.
*/
it('can add node pools with region prices of $0', () => {
const dcSpecificPricingRegion = getRegionById('us-southeast');

const mockCluster = kubernetesClusterFactory.build({
k8s_version: latestKubernetesVersion,
region: dcSpecificPricingRegion.id,
control_plane: {
high_availability: false,
},
});

const mockNewNodePool = nodePoolFactory.build({
count: 2,
type: dcPricingMockLinodeTypes[2].id,
nodes: kubeLinodeFactory.buildList(2),
});

const mockNodePool = nodePoolFactory.build({
count: 1,
type: dcPricingMockLinodeTypes[2].id,
nodes: kubeLinodeFactory.buildList(1),
});

mockGetCluster(mockCluster).as('getCluster');
mockGetClusterPools(mockCluster.id, [mockNodePool]).as('getNodePools');
mockGetKubernetesVersions().as('getVersions');
mockAddNodePool(mockCluster.id, mockNewNodePool).as('addNodePool');
mockGetLinodeType(dcPricingMockLinodeTypes[2]).as('getLinodeType');
mockGetLinodeTypes(dcPricingMockLinodeTypes);
mockGetDashboardUrl(mockCluster.id);
mockGetApiEndpoints(mockCluster.id);

cy.visitWithLogin(`/kubernetes/clusters/${mockCluster.id}`);
cy.wait(['@getCluster', '@getNodePools', '@getVersions', '@getLinodeType']);

// Assert that initial node pool is shown on the page.
cy.findByText('Linode 2 GB', { selector: 'h2' }).should('be.visible');

// Confirm total price of $0 is listed in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');

// Add a new node pool, select plan, submit form in drawer.
ui.button
.findByTitle('Add a Node Pool')
.should('be.visible')
.should('be.enabled')
.click();

mockGetClusterPools(mockCluster.id, [mockNodePool, mockNewNodePool]).as(
'getNodePools'
);

ui.drawer
.findByTitle(`Add a Node Pool: ${mockCluster.label}`)
.should('be.visible')
.within(() => {
cy.findByText('Linode 2 GB')
.should('be.visible')
.closest('tr')
.within(() => {
// Assert that $0 prices are displayed the plan table, then add a node pool with 2 linodes.
cy.findAllByText('$0').should('have.length', 2);
cy.findByLabelText('Add 1').should('be.visible').click().click();
});

// Assert that $0 prices are displayed as helper text.
cy.contains(
'This pool will add $0/month (2 nodes at $0/month) to this cluster.'
).should('be.visible');

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

// Wait for API responses.
cy.wait(['@addNodePool', '@getNodePools']);

// Confirm total price is still $0 in Kube Specs.
cy.findByText('$0.00/month').should('be.visible');
});
});
19 changes: 15 additions & 4 deletions packages/manager/cypress/e2e/core/linodes/backup-linode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,16 +270,22 @@ describe('"Enable Linode Backups" banner', () => {
// See `dcPricingMockLinodeTypes` exported from `support/constants/dc-specific-pricing.ts`.
linodeFactory.build({
label: randomLabel(),
region: 'us-east',
region: 'us-ord',
backups: { enabled: false },
type: dcPricingMockLinodeTypesForBackups[0].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-west',
region: 'us-east',
backups: { enabled: false },
type: dcPricingMockLinodeTypesForBackups[1].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-west',
backups: { enabled: false },
type: dcPricingMockLinodeTypesForBackups[2].id,
}),
linodeFactory.build({
label: randomLabel(),
region: 'us-central',
Expand Down Expand Up @@ -317,6 +323,7 @@ describe('"Enable Linode Backups" banner', () => {

// The expected backup price for each Linode, as shown in backups drawer table.
const expectedPrices = [
'$0.00/mo', // us-ord mocked price.
'$3.57/mo', // us-east mocked price.
'$4.17/mo', // us-west mocked price.
'$2.00/mo', // regular price.
Expand Down Expand Up @@ -358,7 +365,7 @@ describe('"Enable Linode Backups" banner', () => {
);

// Confirm that expected total cost is shown.
cy.contains(`Total for 3 Linodes: ${expectedTotal}`).should(
cy.contains(`Total for 4 Linodes: ${expectedTotal}`).should(
'be.visible'
);

Expand All @@ -377,6 +384,10 @@ describe('"Enable Linode Backups" banner', () => {
.closest('tr')
.within(() => {
cy.findByText(expectedPrice).should('be.visible');
// Confirm no error indicator appears for $0.00 prices.
cy.findByLabelText(
'There was an error loading the price.'
).should('not.exist');
});
});

Expand All @@ -398,7 +409,7 @@ describe('"Enable Linode Backups" banner', () => {
cy.wait([...enableBackupAliases, '@updateAccountSettings']);

ui.toast.assertMessage(
'3 Linodes have been enrolled in automatic backups, and all new Linodes will automatically be backed up.'
'4 Linodes have been enrolled in automatic backups, and all new Linodes will automatically be backed up.'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('VPC assign/unassign flows', () => {
mockGetLinodeConfigs(mockLinode.id, [mockConfig]).as(
'getLinodeConfigs'
);
cy.findByLabelText('Linodes')
cy.findByLabelText('Linode')
.should('be.visible')
.click()
.type(mockLinode.label)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ export const dcPricingMockLinodeTypes = linodeTypeFactory.buildList(3, {
monthly: 12.2,
},
{
hourly: 0.006,
// Mock a DC with $0 region prices, which is possible in some circumstances (e.g. Limited Availability).
hourly: 0.0,
id: 'us-southeast',
monthly: 4.67,
monthly: 0.0,
},
],
});
Expand All @@ -92,6 +93,11 @@ export const dcPricingMockLinodeTypesForBackups = linodeTypeFactory.buildList(
monthly: 2.0,
},
region_prices: [
{
hourly: 0,
id: 'us-ord',
monthly: 0,
},
{
hourly: 0.0048,
id: 'us-east',
Expand Down
4 changes: 2 additions & 2 deletions packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "linode-manager",
"author": "Linode",
"description": "The Linode Manager website",
"version": "1.111.0",
"version": "1.112.0",
"private": true,
"type": "module",
"bugs": {
Expand Down Expand Up @@ -215,4 +215,4 @@
"Firefox ESR",
"not ie < 9"
]
}
}
Loading

0 comments on commit f1d10aa

Please sign in to comment.