-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Sharding Support to PSMDB Demand Backup Test #918
base: main
Are you sure you want to change the base?
Changes from 16 commits
6acb71c
b6427dc
e9180d2
2607526
23ea4d6
50bc595
f13b304
cb0df55
ebed629
f2a47fa
0bd8c2f
209480e
fd15ab1
7d7d9a7
c985e07
9a34e44
428c1de
ab026d4
1d87e7f
48106fd
330c46f
3d04a71
90dc00c
455a015
0cde310
5dd703c
630742c
32e875e
46d144c
ebff4b9
4d5bb14
a63a3db
4663cf4
20c7837
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,344 @@ | ||
// everest | ||
// Copyright (C) 2023 Percona LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import { expect, test } from '@playwright/test'; | ||
import { | ||
deleteDbCluster, | ||
gotoDbClusterBackups, | ||
gotoDbClusterRestores, | ||
} from '@e2e/utils/db-clusters-list'; | ||
import { getTokenFromLocalStorage } from '@e2e/utils/localStorage'; | ||
import { getClusterDetailedInfo } from '@e2e/utils/storage-class'; | ||
import { | ||
moveForward, | ||
submitWizard, | ||
populateBasicInformation, | ||
populateResources, | ||
populateAdvancedConfig, | ||
populateMonitoringModalForm, | ||
} from '@e2e/utils/db-wizard'; | ||
import { EVEREST_CI_NAMESPACES } from '@e2e/constants'; | ||
import { | ||
waitForStatus, | ||
waitForDelete, | ||
findRowAndClickActions, | ||
} from '@e2e/utils/table'; | ||
import { checkError } from '@e2e/utils/generic'; | ||
import { | ||
deleteMonitoringInstance, | ||
listMonitoringInstances, | ||
} from '@e2e/utils/monitoring-instance'; | ||
import { clickOnDemandBackup } from '@e2e/pr/db-cluster-details/utils'; | ||
import { | ||
queryPSMDB, | ||
prepareMongoDBTestDB, | ||
validateMongoDBSharding, | ||
configureMongoDBSharding, | ||
dropTestDB, | ||
queryTestDB, | ||
} from '@e2e/utils/db-cmd-line'; | ||
|
||
export let isShardingEnabled = false; | ||
|
||
const { | ||
MONITORING_URL, | ||
MONITORING_USER, | ||
MONITORING_PASSWORD, | ||
SELECT_DB, | ||
SELECT_SIZE, | ||
} = process.env; | ||
let token: string; | ||
|
||
const db = 'psmdb'; | ||
const size = 3; | ||
|
||
test.describe.configure({ retries: 0 }); | ||
|
||
test.describe( | ||
'Demand backup psmdb', | ||
{ | ||
tag: '@release', | ||
}, | ||
() => { | ||
test.skip( | ||
() => | ||
(SELECT_DB !== db && !!SELECT_DB) || | ||
(SELECT_SIZE !== size.toString() && !!SELECT_SIZE) | ||
); | ||
test.describe.configure({ timeout: 720000 }); | ||
|
||
const clusterName = `${db}-${size}-dembkp`; | ||
|
||
let storageClasses = []; | ||
const namespace = EVEREST_CI_NAMESPACES.EVEREST_UI; | ||
const monitoringName = `${db}-${size}-pmm`; | ||
const baseBackupName = `dembkp-${db}-${size}`; | ||
|
||
test.beforeAll(async ({ request }) => { | ||
token = await getTokenFromLocalStorage(); | ||
|
||
const { storageClassNames = [] } = await getClusterDetailedInfo( | ||
token, | ||
request | ||
); | ||
storageClasses = storageClassNames; | ||
}); | ||
|
||
test.afterAll(async ({ request }) => { | ||
// we try to delete all monitoring instances because cluster creation expects that none exist | ||
// (monitoring instance is added in the form where the warning that none exist is visible) | ||
const monitoringInstances = await listMonitoringInstances( | ||
request, | ||
namespace, | ||
token | ||
); | ||
for (const instance of monitoringInstances) { | ||
await deleteMonitoringInstance( | ||
request, | ||
namespace, | ||
instance.name, | ||
token | ||
); | ||
} | ||
}); | ||
|
||
test(`Cluster creation [${db} size ${size}]`, async ({ page, request }) => { | ||
expect(storageClasses.length).toBeGreaterThan(0); | ||
|
||
await page.goto('/databases'); | ||
await page.getByTestId('add-db-cluster-button').waitFor(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think this part should be moved to utils to avoid duplication 🤔 we call the cluster creation button in a lot of places |
||
await page.getByTestId('add-db-cluster-button').click(); | ||
await page.getByTestId(`add-db-cluster-button-${db}`).click(); | ||
|
||
await test.step('Populate basic information', async () => { | ||
await populateBasicInformation( | ||
page, | ||
namespace, | ||
clusterName, | ||
db, | ||
storageClasses[0], | ||
false | ||
); | ||
}); | ||
|
||
// Step to activate Sharding | ||
await test.step('Activate sharding', async () => { | ||
const shardingCheckbox = page | ||
.getByTestId('switch-input-sharding') | ||
.getByRole('checkbox'); | ||
const isChecked = await shardingCheckbox?.isChecked(); | ||
if (!isChecked) { | ||
if (shardingCheckbox) { | ||
isShardingEnabled = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we create a new function in db-cmd-line.ts we don't need this here. Also we want to check if sharding is actually enabled in the backend (api call) and not did we just click on something in UI. |
||
await shardingCheckbox.click(); | ||
} | ||
} | ||
await expect(shardingCheckbox).toBeChecked(); | ||
}); | ||
|
||
await moveForward(page); // Move forward after activating sharding | ||
|
||
await test.step('Populate resources', async () => { | ||
await page | ||
.getByRole('button') | ||
.getByText(size + ' node') | ||
.click(); | ||
await expect(page.getByText('Nº nodes: ' + size)).toBeVisible(); | ||
await populateResources(page, 0.6, 1, 1, size); | ||
// await moveForward(page); | ||
}); | ||
|
||
// Step to set number of shards | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment since test description below has the same. |
||
await test.step('Set number of shards', async () => { | ||
const shardsInput = await page.getByTestId('text-input-shard-nr'); | ||
await expect(shardsInput).toBeVisible(); | ||
await shardsInput.fill('2'); | ||
await expect(shardsInput).toHaveValue('2'); | ||
await moveForward(page); | ||
}); | ||
|
||
await test.step('Populate backups', async () => { | ||
await moveForward(page); | ||
}); | ||
|
||
await test.step('Populate advanced db config', async () => { | ||
await populateAdvancedConfig(page, db, '', true, ''); | ||
await moveForward(page); | ||
}); | ||
|
||
await test.step('Populate monitoring', async () => { | ||
await populateMonitoringModalForm( | ||
page, | ||
monitoringName, | ||
namespace, | ||
MONITORING_URL, | ||
MONITORING_USER, | ||
MONITORING_PASSWORD, | ||
false | ||
); | ||
await page.getByTestId('switch-input-monitoring').click(); | ||
await expect( | ||
page.getByTestId('text-input-monitoring-instance') | ||
).toHaveValue(monitoringName); | ||
}); | ||
|
||
await test.step('Submit wizard', async () => { | ||
await submitWizard(page); | ||
|
||
await expect( | ||
page.getByText('Awesome! Your database is being created!') | ||
).toBeVisible(); | ||
}); | ||
|
||
// go to db list and check status | ||
await test.step('Check db list and status', async () => { | ||
await page.goto('/databases'); | ||
await waitForStatus(page, clusterName, 'Initializing', 15000); | ||
await waitForStatus(page, clusterName, 'Up', 600000); | ||
}); | ||
|
||
await test.step('Check db cluster k8s object options', async () => { | ||
const response = await request.get( | ||
`/v1/namespaces/${namespace}/database-clusters`, | ||
{ | ||
headers: { | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
} | ||
); | ||
|
||
await checkError(response); | ||
|
||
// TODO: replace with correct payload typings from GET DB Clusters | ||
const { items: clusters } = await response.json(); | ||
|
||
const addedCluster = clusters.find( | ||
(cluster) => cluster.metadata.name === clusterName | ||
); | ||
|
||
expect(addedCluster).not.toBeUndefined(); | ||
expect(addedCluster?.spec.engine.type).toBe(db); | ||
expect(addedCluster?.spec.engine.replicas).toBe(size); | ||
expect(['600m', '0.6']).toContain( | ||
addedCluster?.spec.engine.resources?.cpu.toString() | ||
); | ||
expect(addedCluster?.spec.engine.resources?.memory.toString()).toBe( | ||
'1G' | ||
); | ||
expect(addedCluster?.spec.engine.storage.size.toString()).toBe('1Gi'); | ||
expect(addedCluster?.spec.proxy.expose.type).toBe('internal'); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add something like:
|
||
//if (db != 'psmdb') { | ||
// expect(addedCluster?.spec.proxy.replicas).toBe(size); | ||
//} | ||
Comment on lines
+243
to
+245
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
||
}); | ||
}); | ||
|
||
// Add data in MondoDB | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment since test description below has the same. |
||
test(`Add data [${db} size ${size}]`, async () => { | ||
await prepareMongoDBTestDB(clusterName, namespace); | ||
}); | ||
|
||
// Add MongoDB-specific configuration | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment since test description below has the same. |
||
test(`Setup MongoDB-specific sharding [${db} size ${size}]`, async () => { | ||
await configureMongoDBSharding(clusterName, namespace); | ||
}); | ||
|
||
// Validate Sharding for MongoDB | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment since test description below has the same. |
||
test(`Validate Sharding for MongoDB [${db} size ${size}]`, async () => { | ||
await validateMongoDBSharding(clusterName, namespace); | ||
}); | ||
|
||
test(`Create demand backup [${db} size ${size}]`, async ({ page }) => { | ||
await gotoDbClusterBackups(page, clusterName); | ||
await clickOnDemandBackup(page); | ||
await page.getByTestId('text-input-name').fill(baseBackupName + '-1'); | ||
await expect(page.getByTestId('text-input-name')).not.toBeEmpty(); | ||
await expect( | ||
page.getByTestId('text-input-storage-location') | ||
).not.toBeEmpty(); | ||
await page.getByTestId('form-dialog-create').click(); | ||
|
||
await waitForStatus(page, baseBackupName + '-1', 'Succeeded', 500000); | ||
}); | ||
|
||
test(`Delete data [${db} size ${size}]`, async () => { | ||
await dropTestDB(clusterName, namespace); | ||
}); | ||
|
||
test(`Restore cluster [${db} size ${size}]`, async ({ page }) => { | ||
await gotoDbClusterBackups(page, clusterName); | ||
await findRowAndClickActions( | ||
page, | ||
baseBackupName + '-1', | ||
'Restore to this DB' | ||
); | ||
await expect( | ||
page.getByTestId('select-input-backup-name') | ||
).not.toBeEmpty(); | ||
await page.getByTestId('form-dialog-restore').click(); | ||
|
||
await page.goto('/databases'); | ||
await waitForStatus(page, clusterName, 'Restoring', 30000); | ||
await waitForStatus(page, clusterName, 'Up', 600000); | ||
|
||
await gotoDbClusterRestores(page, clusterName); | ||
// we select based on backup source since restores cannot be named and we don't know | ||
// in advance what will be the name | ||
await waitForStatus(page, baseBackupName + '-1', 'Succeeded', 120000); | ||
}); | ||
|
||
test(`Check data after restore [${db} size ${size}]`, async () => { | ||
// Validate the data in the t1 and t2 | ||
const t1Data = await queryTestDB(clusterName, namespace, 't1'); | ||
expect(t1Data.trim()).toBe('[ { a: 1 }, { a: 2 }, { a: 3 } ]'); | ||
|
||
const t2Data = await queryTestDB(clusterName, namespace, 't2'); | ||
expect(t2Data.trim()).toBe('[ { a: 1 }, { a: 2 }, { a: 3 } ]'); | ||
Comment on lines
+304
to
+309
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this into |
||
|
||
// Validate sharding | ||
const shardingStatus = await queryPSMDB( | ||
clusterName, | ||
namespace, | ||
'admin', | ||
'sh.status();' | ||
); | ||
expect(shardingStatus).toContain('test.t1'); | ||
expect(shardingStatus).toContain('test.t2'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this into |
||
}); | ||
|
||
test(`Delete restore [${db} size ${size}]`, async ({ page }) => { | ||
await gotoDbClusterRestores(page, clusterName); | ||
await findRowAndClickActions(page, baseBackupName + '-1', 'Delete'); | ||
await expect(page.getByLabel('Delete restore')).toBeVisible(); | ||
await page.getByTestId('confirm-dialog-delete').click(); | ||
await waitForDelete(page, baseBackupName + '-1', 15000); | ||
}); | ||
|
||
test(`Delete backup [${db} size ${size}]`, async ({ page }) => { | ||
await gotoDbClusterBackups(page, clusterName); | ||
await findRowAndClickActions(page, baseBackupName + '-1', 'Delete'); | ||
await expect(page.getByLabel('Delete backup')).toBeVisible(); | ||
await page.getByTestId('form-dialog-delete').click(); | ||
await waitForDelete(page, baseBackupName + '-1', 30000); | ||
}); | ||
|
||
test(`Delete cluster [${db} size ${size}]`, async ({ page }) => { | ||
await deleteDbCluster(page, clusterName); | ||
await waitForStatus(page, clusterName, 'Deleting', 15000); | ||
await waitForDelete(page, clusterName, 240000); | ||
}); | ||
} | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems strange exporting this from the test and importing it into helper functions. Let's create a function
psmdbShardingEnabled
in db-cmd-line.ts and then use it inside the same file.