diff --git a/ui/apps/everest/.e2e/constants.ts b/ui/apps/everest/.e2e/constants.ts index f1a7986e0..a5a1f0fdf 100644 --- a/ui/apps/everest/.e2e/constants.ts +++ b/ui/apps/everest/.e2e/constants.ts @@ -23,6 +23,7 @@ const minute = 60 * second; export enum TIMEOUTS { TenSeconds = 10 * second, + FifteenSeconds = 15 * second, ThirtySeconds = 30 * second, OneMinute = minute, ThreeMinutes = 3 * minute, diff --git a/ui/apps/everest/.e2e/playwright.config.ts b/ui/apps/everest/.e2e/playwright.config.ts index d453151dd..38764f352 100644 --- a/ui/apps/everest/.e2e/playwright.config.ts +++ b/ui/apps/everest/.e2e/playwright.config.ts @@ -117,6 +117,7 @@ export default defineConfig({ channel: 'chrome', storageState: STORAGE_STATE_FILE, video: 'retain-on-failure', + actionTimeout: 10000, }, testDir: 'upgrade', dependencies: ['setup'], diff --git a/ui/apps/everest/.e2e/release/demand-backup.e2e.ts b/ui/apps/everest/.e2e/release/demand-backup.e2e.ts index cd0935bf6..40b8e3c24 100644 --- a/ui/apps/everest/.e2e/release/demand-backup.e2e.ts +++ b/ui/apps/everest/.e2e/release/demand-backup.e2e.ts @@ -35,13 +35,13 @@ import { 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 { prepareTestDB, dropTestDB, queryTestDB } from '@e2e/utils/db-cmd-line'; +import { getDbClusterAPI } from '@e2e/utils/db-cluster'; const { MONITORING_URL, @@ -181,25 +181,13 @@ test.describe.configure({ retries: 0 }); }); 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 + const addedCluster = await getDbClusterAPI( + clusterName, + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token ); - expect(addedCluster).not.toBeUndefined(); expect(addedCluster?.spec.engine.type).toBe(db); expect(addedCluster?.spec.engine.replicas).toBe(size); expect(['600m', '0.6']).toContain( diff --git a/ui/apps/everest/.e2e/release/init-deploy.e2e.ts b/ui/apps/everest/.e2e/release/init-deploy.e2e.ts index 4eff0526a..b6ff1d0c2 100644 --- a/ui/apps/everest/.e2e/release/init-deploy.e2e.ts +++ b/ui/apps/everest/.e2e/release/init-deploy.e2e.ts @@ -32,11 +32,11 @@ import { } from '@e2e/utils/db-wizard'; import { EVEREST_CI_NAMESPACES } from '@e2e/constants'; import { waitForStatus, waitForDelete } from '@e2e/utils/table'; -import { checkError } from '@e2e/utils/generic'; import { deleteMonitoringInstance, listMonitoringInstances, } from '@e2e/utils/monitoring-instance'; +import { getDbClusterAPI } from '@e2e/utils/db-cluster'; const { MONITORING_URL, @@ -183,24 +183,13 @@ test.describe.configure({ retries: 0 }); }); 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 + const addedCluster = await getDbClusterAPI( + clusterName, + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token ); - expect(addedCluster).not.toBeUndefined(); expect(addedCluster?.spec.engine.type).toBe(db); expect(addedCluster?.spec.engine.replicas).toBe(size); expect(['600m', '0.6']).toContain( diff --git a/ui/apps/everest/.e2e/release/pitr.e2e.ts b/ui/apps/everest/.e2e/release/pitr.e2e.ts index f702410df..30d822942 100644 --- a/ui/apps/everest/.e2e/release/pitr.e2e.ts +++ b/ui/apps/everest/.e2e/release/pitr.e2e.ts @@ -36,7 +36,6 @@ import { waitForDelete, findRowAndClickActions, } from '@e2e/utils/table'; -import { checkError } from '@e2e/utils/generic'; import { deleteMonitoringInstance, listMonitoringInstances, @@ -50,6 +49,7 @@ import { pgInsertDummyTestDB, } from '@e2e/utils/db-cmd-line'; import { addFirstScheduleInDBWizard } from '@e2e/pr/db-cluster/db-wizard/db-wizard-utils'; +import { getDbClusterAPI, updateDbClusterAPI } from '@e2e/utils/db-cluster'; const { MONITORING_URL, @@ -253,45 +253,32 @@ test.describe.configure({ retries: 0 }); if (db !== 'psmdb') { return; } - let psmdbCluster = await request.get( - `/v1/namespaces/${namespace}/database-clusters/${clusterName}` - ); - - await checkError(psmdbCluster); - const psmdbPayload = await psmdbCluster.json(); - - psmdbPayload.spec.backup.pitr.uploadIntervalSec = 60; - const updatedPSMDBCluster = await request.put( - `/v1/namespaces/${namespace}/database-clusters/${clusterName}`, - { - data: psmdbPayload, - } + const psmdbCluster = await getDbClusterAPI( + clusterName, + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token + ); + psmdbCluster.spec.backup.pitr.uploadIntervalSec = 60; + await updateDbClusterAPI( + clusterName, + EVEREST_CI_NAMESPACES.EVEREST_UI, + psmdbCluster, + request, + token ); - - await checkError(updatedPSMDBCluster); }); 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 + const addedCluster = await getDbClusterAPI( + clusterName, + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token ); expect(addedCluster?.spec.backup.pitr.enabled).toBe(true); - expect(addedCluster).not.toBeUndefined(); expect(addedCluster?.spec.engine.type).toBe(db); expect(addedCluster?.spec.engine.replicas).toBe(size); expect(['600m', '0.6']).toContain( diff --git a/ui/apps/everest/.e2e/release/scheduled-backup.e2e.ts b/ui/apps/everest/.e2e/release/scheduled-backup.e2e.ts index 822321480..3a98aa56f 100644 --- a/ui/apps/everest/.e2e/release/scheduled-backup.e2e.ts +++ b/ui/apps/everest/.e2e/release/scheduled-backup.e2e.ts @@ -39,13 +39,13 @@ import { waitForDelete, findRowAndClickActions, } from '@e2e/utils/table'; -import { checkError } from '@e2e/utils/generic'; import { deleteMonitoringInstance, listMonitoringInstances, } from '@e2e/utils/monitoring-instance'; import { clickCreateSchedule } from '@e2e/pr/db-cluster-details/utils'; import { prepareTestDB, dropTestDB, queryTestDB } from '@e2e/utils/db-cmd-line'; +import { getDbClusterAPI } from '@e2e/utils/db-cluster'; const { MONITORING_URL, @@ -188,25 +188,13 @@ function getNextScheduleMinute(incrementMinutes: number): string { }); 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 + const addedCluster = await getDbClusterAPI( + clusterName, + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token ); - expect(addedCluster).not.toBeUndefined(); expect(addedCluster?.spec.engine.type).toBe(db); expect(addedCluster?.spec.engine.replicas).toBe(size); expect(['600m', '0.6']).toContain( diff --git a/ui/apps/everest/.e2e/upgrade/post-upgrade.e2e.ts b/ui/apps/everest/.e2e/upgrade/post-upgrade.e2e.ts index b3fbbc887..90708cb56 100644 --- a/ui/apps/everest/.e2e/upgrade/post-upgrade.e2e.ts +++ b/ui/apps/everest/.e2e/upgrade/post-upgrade.e2e.ts @@ -1,26 +1,43 @@ import { expect, test } from '@playwright/test'; import fs from 'fs'; -import { everestdir, TIMEOUTS } from '@e2e/constants'; +import { EVEREST_CI_NAMESPACES, TIMEOUTS } from '@e2e/constants'; import { expectedEverestUpgradeLog, mongoDBCluster, postgresDBCluster, + pxcDBCluster, } from './testData'; -import { waitForStatus } from '@e2e/utils/table'; +import { waitForStatus, waitForDelete } from '@e2e/utils/table'; import { getTokenFromLocalStorage } from '@e2e/utils/localStorage'; import { getNamespacesFn } from '@e2e/utils/namespaces'; import { getExpectedOperatorVersions } from '@e2e/upgrade/helper'; +import { getDbAvailableUpgradeVersionK8S } from '@e2e/utils/db-cluster'; +import { + deleteDbCluster, + getDbClustersListAPI, +} from '@e2e/utils/db-clusters-list'; +import { queryTestDB } from '@e2e/utils/db-cmd-line'; let namespace: string; +let token: string; +let upgradeClustersInfo: { + name: string; + namespace: string; + dbType: string; + currentVersion: string; + upgradeVersion: string | null; +}[] = []; test.describe('Post upgrade tests', { tag: '@post-upgrade' }, async () => { + test.describe.configure({ timeout: TIMEOUTS.FifteenMinutes }); + test.beforeAll(async ({ request }) => { - const token = await getTokenFromLocalStorage(); + token = await getTokenFromLocalStorage(); [namespace] = await getNamespacesFn(token, request); }); test('Verify upgrade.log file', async () => { - const filePath = `${everestdir}/ui/apps/everest/.e2e/upgrade.log`; + const filePath = `/tmp/everest-upgrade.log`; const data = fs.readFileSync(filePath, 'utf8'); const expectedText = expectedEverestUpgradeLog(); @@ -28,10 +45,9 @@ test.describe('Post upgrade tests', { tag: '@post-upgrade' }, async () => { }); test('Verify DB clusters are running', async ({ page }) => { - // go to db list and check status await page.goto('/databases'); - await test.step('verify mongoDB and postgresDB clusters are up', async () => { + await test.step('Verify clusters are up', async () => { await waitForStatus( page, mongoDBCluster.name, @@ -44,25 +60,30 @@ test.describe('Post upgrade tests', { tag: '@post-upgrade' }, async () => { 'Up', TIMEOUTS.ThirtySeconds ); + await waitForStatus( + page, + pxcDBCluster.name, + 'Up', + TIMEOUTS.ThirtySeconds + ); }); }); - test('verify user is able to upgrade operators', async ({ page }) => { + test('Verify operators upgrade', async ({ page }) => { const upgradeOperatorsButton = page.getByRole('button', { name: 'Upgrade Operators', }); const upgradeOperatorsModal = page.getByRole('dialog'); - const operatorsVersions = await getExpectedOperatorVersions(); test.skip(operatorsVersions.length === 0, 'No operators to upgrade'); - await test.step(`open ${namespace} namespace settings`, async () => { + await test.step(`Open ${namespace} namespace settings`, async () => { await page.goto(`/settings/namespaces/${namespace}`); await expect(upgradeOperatorsButton).toBeVisible(); }); - await test.step(`verify "upgrade available" text is present in the header`, async () => { + await test.step(`Verify "upgrade available" text is present in the header`, async () => { for (const operator of operatorsVersions) { await expect( page.getByText( @@ -72,7 +93,7 @@ test.describe('Post upgrade tests', { tag: '@post-upgrade' }, async () => { } }); - await test.step(`click upgrade button and verify modal contains correct versions and operators`, async () => { + await test.step(`Click upgrade button and verify modal contains correct versions and operators`, async () => { await upgradeOperatorsButton.click(); await expect( upgradeOperatorsModal.getByText( @@ -89,7 +110,7 @@ test.describe('Post upgrade tests', { tag: '@post-upgrade' }, async () => { } }); - await test.step(`click Upgrade and wait for upgrade success`, async () => { + await test.step(`Click Upgrade and wait for upgrade success`, async () => { await upgradeOperatorsModal .getByRole('button', { name: 'Upgrade' }) .click(); @@ -104,5 +125,167 @@ test.describe('Post upgrade tests', { tag: '@post-upgrade' }, async () => { }).toPass({ timeout: TIMEOUTS.ThreeMinutes }); } }); + + for (const operator of operatorsVersions) { + await test.step(`Upgrade CR version for [${operator.shortName}]`, async () => { + await page.goto(`/settings/namespaces/${namespace}`); + await waitForStatus( + page, + `${operator.shortName}-db-cluster`, + 'Up', + TIMEOUTS.ThirtySeconds + ); + + await page + .getByRole('button', { + name: `Database needs restart to use CRVersion '${operator.version.replace('v', '')}'`, + }) + .click(); + + const upgradeCRDModal = page.getByRole('dialog'); + + await expect( + upgradeCRDModal.getByText( + `Are you sure you want to upgrade your CRD (Custom Resource Definition) to version ${operator.version.replace('v', '')} in ${operator.shortName}-db-cluster cluster?` + ) + ).toBeVisible(); + + await upgradeCRDModal.getByRole('button', { name: 'Upgrade' }).click(); + + await waitForStatus( + page, + `${operator.shortName}-db-cluster`, + 'Initializing', + TIMEOUTS.ThreeMinutes + ); + await waitForStatus( + page, + `${operator.shortName}-db-cluster`, + 'Up', + TIMEOUTS.ThreeMinutes + ); + }); + } + }); + + test('Verify databases upgrade', async ({ page, request }) => { + await page.goto('/databases'); + + const dbClusters = ( + await getDbClustersListAPI( + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token + ) + ).items; + + upgradeClustersInfo = dbClusters.map((c) => { + return { + name: c.metadata.name, + namespace: c.metadata.namespace, + dbType: c.spec.engine.type, + currentVersion: c.spec.engine.version, + }; + }); + + for (const c of upgradeClustersInfo) { + c.upgradeVersion = await getDbAvailableUpgradeVersionK8S( + `${c.dbType}-db-cluster`, + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token + ); + } + const upgradeCount = upgradeClustersInfo.filter( + (item) => item.upgradeVersion !== null + ).length; + + test.skip(upgradeCount === 0, 'No databases to upgrade'); + + for (const c of upgradeClustersInfo.filter( + (item) => item.upgradeVersion !== null + )) { + await test.step(`Upgrade ${c.name} database`, async () => { + await page.goto(`/databases/${c.namespace}/${c.name}/overview`); + await page.getByTestId('upgrade-db-btn').click(); + const upgradeDbVersionModal = page.getByRole('dialog'); + + await expect( + upgradeDbVersionModal.getByText(`Upgrade DB version`) + ).toBeVisible(); + await upgradeDbVersionModal + .getByTestId('select-db-version-button') + .click(); + await page.getByRole('option', { name: `${c.upgradeVersion}` }).click(); + + await upgradeDbVersionModal.getByTestId('form-dialog-upgrade').click(); + }); + + await test.step(`Wait for database [${c.name}] status after upgrade`, async () => { + await page.goto('/databases'); + await waitForStatus( + page, + `${c.name}`, + 'Initializing', + TIMEOUTS.ThreeMinutes + ); + await waitForStatus(page, `${c.name}`, 'Up', TIMEOUTS.ThreeMinutes); + }); + } + + await test.step(`Check databases were upgraded`, async () => { + await page.goto('/databases'); + + for (const c of upgradeClustersInfo) { + let tech: string = ''; + + switch (c.dbType) { + case 'pxc': { + tech = 'MySQL'; + break; + } + case 'psmdb': { + tech = 'MongoDB'; + break; + } + case 'postgresql': { + tech = 'PostgreSQL'; + break; + } + } + + const version = + c.upgradeVersion === null ? c.currentVersion : c.upgradeVersion; + await expect( + page.getByRole('row').filter({ hasText: `${c.name}` }) + ).toContainText(`${tech} ${version}`); + } + }); + }); + + test(`Check data after upgrade`, async ({ page }) => { + for (const c of upgradeClustersInfo) { + const result = await queryTestDB(c.name, c.namespace); + + switch (c.dbType) { + case 'pxc': + expect(result.trim()).toBe('1\n2\n3'); + break; + case 'psmdb': + expect(result.trim()).toBe('[ { a: 1 }, { a: 2 }, { a: 3 } ]'); + break; + case 'postgresql': + expect(result.trim()).toBe('1\n 2\n 3'); + break; + } + } + }); + + test(`Delete clusters`, async ({ page }) => { + for (const c of upgradeClustersInfo) { + await deleteDbCluster(page, c.name); + await waitForStatus(page, c.name, 'Deleting', TIMEOUTS.FifteenSeconds); + await waitForDelete(page, c.name, TIMEOUTS.FiveMinutes); + } }); }); diff --git a/ui/apps/everest/.e2e/upgrade/pre-upgrade.e2e.ts b/ui/apps/everest/.e2e/upgrade/pre-upgrade.e2e.ts index 50e368069..4d7f5b90b 100644 --- a/ui/apps/everest/.e2e/upgrade/pre-upgrade.e2e.ts +++ b/ui/apps/everest/.e2e/upgrade/pre-upgrade.e2e.ts @@ -1,26 +1,34 @@ import { expect, test } from '@playwright/test'; import { createDbClusterFn } from '@e2e/utils/db-cluster'; -import { mongoDBCluster, postgresDBCluster } from './testData'; -import { getDBClustersList } from '@e2e/utils/db-clusters-list'; +import { pxcDBCluster, mongoDBCluster, postgresDBCluster } from './testData'; +import { getDbClustersListAPI } from '@e2e/utils/db-clusters-list'; import { TIMEOUTS } from '@e2e/constants'; +import { EVEREST_CI_NAMESPACES } from '@e2e/constants'; +import { getTokenFromLocalStorage } from '@e2e/utils/localStorage'; +import { prepareTestDB } from '@e2e/utils/db-cmd-line'; test.describe.configure({ retries: 0 }); +test.describe.configure({ timeout: TIMEOUTS.FifteenMinutes }); + test( 'Pre upgrade setup', { tag: '@pre-upgrade' }, async ({ page, request }) => { - // await createDbClusterFn(request, { - // dbName: psDBCluster.name, - // dbType: 'mysql', - // numberOfNodes: psDBCluster.numberOfNodes, - // cpu: psDBCluster.cpu, - // disk: psDBCluster.disk, - // memory: psDBCluster.memory, - // externalAccess: psDBCluster.externalAccess, - // sourceRanges: psDBCluster.sourceRanges, - // }); + const token = await getTokenFromLocalStorage(); + + await test.step('Create DB clusters', async () => { + await createDbClusterFn(request, { + dbName: pxcDBCluster.name, + dbType: 'mysql', + numberOfNodes: pxcDBCluster.numberOfNodes, + numberOfProxies: pxcDBCluster.numberOfProxies, + cpu: pxcDBCluster.cpu, + disk: pxcDBCluster.disk, + memory: pxcDBCluster.memory, + externalAccess: pxcDBCluster.externalAccess, + //sourceRanges: pxcDBCluster.sourceRanges, + }); - await test.step('Create DB clusers', async () => { await createDbClusterFn(request, { dbName: mongoDBCluster.name, dbType: 'mongodb', @@ -35,6 +43,7 @@ test( dbName: postgresDBCluster.name, dbType: 'postgresql', numberOfNodes: postgresDBCluster.numberOfNodes, + numberOfProxies: postgresDBCluster.numberOfProxies, cpu: postgresDBCluster.cpu, disk: postgresDBCluster.disk, memory: postgresDBCluster.disk, @@ -44,21 +53,51 @@ test( await page.waitForTimeout(TIMEOUTS.TenSeconds); }); - await expect(async () => { - const dbClusters = (await getDBClustersList(request)).items; + await test.step('Wait for databases to become ready', async () => { + await expect(async () => { + const dbClusters = ( + await getDbClustersListAPI( + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token + ) + ).items; + + const clustersInfo = dbClusters.map((c) => { + return { status: c.status.status, name: c.metadata.name }; + }); + + clustersInfo.forEach((c) => { + expect(c.status, `expecting ${c.name} to have "ready" status`).toBe( + 'ready' + ); + }); + }, 'waiting for db clusters to be "ready"').toPass({ + timeout: TIMEOUTS.TenMinutes, + intervals: [TIMEOUTS.OneMinute], + }); + }); + + await test.step('Add data to databases', async () => { + const dbClusters = ( + await getDbClustersListAPI( + EVEREST_CI_NAMESPACES.EVEREST_UI, + request, + token + ) + ).items; const clustersInfo = dbClusters.map((c) => { - return { status: c.status.status, name: c.metadata.name }; + return { + status: c.status.status, + name: c.metadata.name, + namespace: c.metadata.namespace, + }; }); clustersInfo.forEach((c) => { - expect(c.status, `expecting ${c.name} to have "ready" status`).toBe( - 'ready' - ); + prepareTestDB(c.name, c.namespace); }); - }, 'waiting for db clusters to be "ready"').toPass({ - timeout: TIMEOUTS.TenMinutes, - intervals: [TIMEOUTS.OneMinute], }); } ); diff --git a/ui/apps/everest/.e2e/upgrade/testData.ts b/ui/apps/everest/.e2e/upgrade/testData.ts index e703022b6..5fd2a0593 100644 --- a/ui/apps/everest/.e2e/upgrade/testData.ts +++ b/ui/apps/everest/.e2e/upgrade/testData.ts @@ -1,46 +1,49 @@ import { everestTagForUpgrade } from '@e2e/constants'; export const pxcDBCluster = { - name: 'ps-db-cluster', - numberOfNodes: 1, + name: 'pxc-db-cluster', + numberOfNodes: 3, + numberOfProxies: 2, cpu: 0.6, - disk: 1, + disk: 5, memory: 1, - externalAccess: true, - sourceRanges: [ - { - sourceRange: 'http://192.168.1.1', - }, - ], + externalAccess: false, + // sourceRanges: [ + // { + // sourceRange: 'http://192.168.1.1', + // }, + // ], }; export const mongoDBCluster = { - name: 'mongo-db-cluster', + name: 'psmdb-db-cluster', numberOfNodes: 3, - cpu: 1, - disk: 1, + cpu: 0.6, + disk: 5, memory: 1, - externalAccess: true, + externalAccess: false, }; export const postgresDBCluster = { - name: 'postgres-db-cluster', - numberOfNodes: 1, - cpu: 1, - disk: 1, + name: 'postgresql-db-cluster', + numberOfNodes: 3, + numberOfProxies: 2, + cpu: 0.6, + disk: 5, memory: 1, - externalAccess: true, + externalAccess: false, }; export const expectedEverestUpgradeLog = ( tag = everestTagForUpgrade.replace(/v/g, '') ) => { - return `✓ Upgrade Operator Lifecycle Manager -✓ Upgrade Percona Catalog -✓ Wait for Everest Operator InstallPlan -✓ Upgrade Everest API server -✓ Upgrade Everest Operator -✓ Run post-upgrade tasks + return `ℹ️ Upgrading Everest to version ${tag} + +✓ Upgrading Custom Resource Definitions +✓ Upgrading Helm chart +✓ Ensuring Everest API deployment is ready +✓ Ensuring Everest operator deployment is ready +✓ Ensuring Everest CatalogSource is ready 🚀 Everest has been upgraded to version ${tag} diff --git a/ui/apps/everest/.e2e/utils/db-cluster.ts b/ui/apps/everest/.e2e/utils/db-cluster.ts index d756c00ac..84991e630 100644 --- a/ui/apps/everest/.e2e/utils/db-cluster.ts +++ b/ui/apps/everest/.e2e/utils/db-cluster.ts @@ -20,6 +20,8 @@ import { getClusterDetailedInfo } from './storage-class'; import { getTokenFromLocalStorage } from './localStorage'; import { getNamespacesFn } from './namespaces'; import { DbType } from '@percona/types'; +import { checkError, getVersionServiceURL } from '@e2e/utils/generic'; +import { execSync } from 'child_process'; export const createDbClusterFn = async ( request: APIRequestContext, @@ -160,3 +162,96 @@ export const deleteDbClusterFn = async ( ); expect(deleteResponse.ok()).toBeTruthy(); }; + +export const getDbClusterAPI = async ( + clusterName: string, + namespace: string, + request: APIRequestContext, + token: string +) => { + const response = await request.get( + `/v1/namespaces/${namespace}/database-clusters/${clusterName}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + await checkError(response); + + return response.json(); +}; + +export const updateDbClusterAPI = async ( + clusterName: string, + namespace: string, + payload: object, + request: APIRequestContext, + token: string +) => { + const updatedCluster = await request.put( + `/v1/namespaces/${namespace}/database-clusters/${clusterName}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + data: payload, + } + ); + await checkError(updatedCluster); +}; + +export const getDbAvailableUpgradeVersionK8S = async ( + clusterName: string, + namespace: string, + request: APIRequestContext, + token: string +) => { + const cluster = await getDbClusterAPI(clusterName, namespace, request, token); + const dbCurrentVersion = cluster.spec.engine.version; + const dbSplitVersion = dbCurrentVersion.split('.'); + const dbType = cluster.spec.engine.type; + const crVersion = cluster.status.crVersion; + + const dbOperatorName = + dbType === 'postgresql' ? 'pg-operator' : dbType + '-operator'; + + const versionServiceURL = await getVersionServiceURL(); + const dbMajorVersion = + dbType === 'postgresql' + ? dbSplitVersion[0] + : dbSplitVersion[0] + '.' + dbSplitVersion[1]; + + try { + const response = await ( + await request.get( + versionServiceURL + + `/versions/v1/${dbOperatorName}/${crVersion}/${dbMajorVersion}-latest` + ) + ).json(); + + // Navigate to the versions array + const versions = response?.versions; + if (!Array.isArray(versions)) return null; + + // Find the first item in the versions array + const dbOperator = versions.find( + (item: any) => item.product === `${dbOperatorName}` + ); + if (!dbOperator) return null; + + // Access the matrix -> mongod/pxc/postgresql key + const vsKey = dbType === 'psmdb' ? 'mongod' : dbType; + const vsDatabase = dbOperator?.matrix?.[vsKey]; + + if (!vsDatabase || typeof vsDatabase !== 'object') return null; + + // Get the first key in the mongod/pxc/postgresql object (e.g., "7.0.14-8") + const dbUpgradeVersion = Object.keys(vsDatabase)[0]; + + return dbUpgradeVersion === dbCurrentVersion ? null : dbUpgradeVersion; + } catch (error) { + console.error('Error extracting database version:', error); + return null; + } +}; diff --git a/ui/apps/everest/.e2e/utils/db-clusters-list.ts b/ui/apps/everest/.e2e/utils/db-clusters-list.ts index 990cd5530..ce4360ff4 100644 --- a/ui/apps/everest/.e2e/utils/db-clusters-list.ts +++ b/ui/apps/everest/.e2e/utils/db-clusters-list.ts @@ -15,14 +15,13 @@ import { APIRequestContext, expect, Page } from '@playwright/test'; import { findRowAndClickActions } from './table'; -import { getTokenFromLocalStorage } from './localStorage'; -import { getNamespacesFn } from './namespaces'; - -export const getDBClustersList = async (request: APIRequestContext) => { - const token = await getTokenFromLocalStorage(); - const namespaces = await getNamespacesFn(token, request); - const namespace = namespaces[0]; +import { checkError } from '@e2e/utils/generic'; +export const getDbClustersListAPI = async ( + namespace: string, + request: APIRequestContext, + token: string +) => { const response = await request.get( `/v1/namespaces/${namespace}/database-clusters`, { @@ -31,10 +30,8 @@ export const getDBClustersList = async (request: APIRequestContext) => { }, } ); + await checkError(response); - expect(response.status()).toBe(200); - - expect(response.ok()).toBeTruthy(); return response.json(); }; diff --git a/ui/apps/everest/.e2e/utils/generic.ts b/ui/apps/everest/.e2e/utils/generic.ts index 180ccb1e1..f2d93501a 100644 --- a/ui/apps/everest/.e2e/utils/generic.ts +++ b/ui/apps/everest/.e2e/utils/generic.ts @@ -14,10 +14,23 @@ // limitations under the License. import { expect } from '@playwright/test'; +import { execSync } from 'child_process'; export const checkError = async (response) => { if (!response.ok()) { console.log(`${response.url()}: `, await response.json()); } + expect(response.status()).toBe(200); expect(response.ok()).toBeTruthy(); }; + +export const getVersionServiceURL = async () => { + try { + const command = `kubectl get deployment everest-server --namespace everest-system -o jsonpath="{.spec.template.spec.containers[0].env[?(@.name=='VERSION_SERVICE_URL')].value}"`; + const output = execSync(command).toString(); + return output; + } catch (error) { + console.error(`Error executing command: ${error}`); + throw error; + } +};