Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ Feature('Test PMM server with srv volume and password enw variable');
let testCaseName = '';
const dockerVersion = process.env.DOCKER_VERSION || 'perconalab/pmm-server:3-dev-latest';

const runContainerWithPasswordVariableUpgrade = async (I) => {
const runContainerWithPasswordVariableUpgrade = async (I, image = dockerVersion, prepareYum = true) => {
await I.verifyCommand('mkdir $HOME/srvPasswordUpgrade || true');
await I.verifyCommand('chmod -R 777 $HOME/srvPasswordUpgrade/ || true');
await I.verifyCommand(`docker run -v $HOME/srvPasswordUpgrade:/srv -d -e GF_SECURITY_ADMIN_PASSWORD=newpass -e PMM_ENABLE_INTERNAL_PG_QAN=1 --restart always --publish 8089:8080 --name pmm-server-password-upgrade ${dockerVersion}`);
await I.verifyCommand(`docker run -v $HOME/srvPasswordUpgrade:/srv -d -e GF_SECURITY_ADMIN_PASSWORD=newpass -e PMM_ENABLE_INTERNAL_PG_QAN=1 --restart always --publish 8089:8080 --name pmm-server-password-upgrade ${image}`);
I.wait(30);

if (!prepareYum) {
return;
}

await I.verifyCommand('docker exec pmm-server-password-upgrade yum update -y percona-release');
await I.verifyCommand('docker exec pmm-server-password-upgrade sed -i\'\' -e \'s^/release/^/experimental/^\' /etc/yum.repos.d/pmm3-server.repo');
await I.verifyCommand('docker exec pmm-server-password-upgrade percona-release enable percona experimental');
Expand Down Expand Up @@ -91,9 +96,11 @@ Scenario(
await I.Authorize('admin', 'newpass', basePmmUrl);
await I.amOnPage(basePmmUrl + homePage.url);
await I.waitForElement(homePage.fields.dashboardHeaderLocator, 60);
const { versionMinor } = await homePage.getVersions();

await homePage.upgradePMM(versionMinor, 'pmm-server-password-upgrade');
await homePage.upgradePMMViaDocker(
'pmm-server-password-upgrade',
async (I, image) => runContainerWithPasswordVariableUpgrade(I, image, false),
);
await I.unAuthorize();
await I.wait(5);
await I.Authorize('admin', 'newpass', basePmmUrl);
Expand Down
97 changes: 97 additions & 0 deletions codeceptjs-e2e/tests/helper/dockerUpgradeHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
function getTargetImage() {
const dockerVersion = process.env.DOCKER_VERSION;

if (dockerVersion && dockerVersion.includes('pmm-server')) {
return dockerVersion;
}

if (process.env.PMM_SERVER_LATEST) {
return `percona/pmm-server:${process.env.PMM_SERVER_LATEST}`;
}

return 'percona/pmm-server:3';
}

function isGuiUpgradeRemoved() {
const version = process.env.PMM_SERVER_LATEST || process.env.DOCKER_VERSION || '';
const match = version.match(/3\.(\d+)/);

return Boolean(match && Number(match[1]) >= 9);
}

function shellQuote(value) {
return `'${String(value).replace(/'/g, `'\\''`)}'`;
}

function buildDockerRunCommand(containerName, targetImage, inspect) {
const hostConfig = inspect.HostConfig;
const config = inspect.Config;
const args = ['docker run -d'];

args.push(`--restart ${hostConfig.RestartPolicy?.Name || 'always'}`);
args.push(`--name ${containerName}`);

if (config.Hostname) {
args.push(`--hostname ${config.Hostname}`);
}

for (const [containerPort, bindings] of Object.entries(hostConfig.PortBindings || {})) {
const port = containerPort.split('/')[0];

for (const binding of bindings) {
args.push(`-p ${binding.HostPort}:${port}`);
}
}

for (const bind of hostConfig.Binds || []) {
args.push(`-v ${bind}`);
}

for (const env of config.Env || []) {
args.push(`-e ${shellQuote(env)}`);
}

const networkMode = hostConfig.NetworkMode;

if (networkMode && networkMode !== 'default' && networkMode !== 'bridge' && !networkMode.startsWith('container:')) {
args.push(`--network ${networkMode}`);
}

args.push(targetImage);

return args.join(' ');
}

async function upgradeContainer(I, containerName, recreateFn) {
const targetImage = getTargetImage();
let inspect = null;

try {
const raw = await I.verifyCommand(`docker inspect ${containerName}`);

inspect = JSON.parse(raw)[0];
} catch (error) {
inspect = null;
}

await I.verifyCommand(`docker pull ${targetImage}`);
await I.verifyCommand(`docker stop ${containerName} || true`);
await I.verifyCommand(`docker rm ${containerName} || true`);

if (recreateFn) {
await recreateFn(I, targetImage);
} else if (inspect) {
await I.verifyCommand(buildDockerRunCommand(containerName, targetImage, inspect));
} else {
throw new Error(`docker upgrade: cannot recreate ${containerName}`);
}

I.wait(90);
}

module.exports = {
buildDockerRunCommand,
getTargetImage,
isGuiUpgradeRemoved,
upgradeContainer,
};
46 changes: 43 additions & 3 deletions codeceptjs-e2e/tests/pages/homePage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { I, dashboardPage, pmmUpgradePage } = inject();
const assert = require('assert');
const moment = require('moment');
const dockerUpgradeHelper = require('../helper/dockerUpgradeHelper');
const productTourModal = require('./components/productTourComponent');
const updatesAvailableDialog = require('./components/updatesAvailableModal');

Expand Down Expand Up @@ -114,7 +115,23 @@ module.exports = {
I.waitForVisible(I.useDataQA('data-testid Nav menu item'), 20);
},

async upgradePMM(version, containerName, skipUpgradeLogs = false) {
isGuiUpgradeRemoved() {
return dockerUpgradeHelper.isGuiUpgradeRemoved();
},

async upgradePMMViaDocker(containerName = this.pmmServerName, recreateFn) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a pipeline that does upgrade via Docker there is no need to have it inside of tests.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await dockerUpgradeHelper.upgradeContainer(I, containerName, recreateFn);

// eslint-disable-next-line no-console
console.log(`Upgraded ${containerName} to: ${dockerUpgradeHelper.getTargetImage()}`);
},

async upgradePMM(version, containerName = this.pmmServerName) {
if (this.isGuiUpgradeRemoved()) {
await this.upgradePMMViaDocker(containerName);
return;
}

const locators = this.getLocators(version);

I.waitForElement(locators.triggerUpdate, 180);
Expand All @@ -139,6 +156,13 @@ module.exports = {
console.log(`Upgraded to pmm server tag: ${await I.verifyCommand('docker ps -a | grep pmm-server | awk -F "pmm-server:" \'{print $2}\' | awk -F " " \'{print $1}\'')}`);
},

async verifyGuiUpgradeRemoved() {
I.amOnPage(pmmUpgradePage.url);
I.switchTo();
I.dontSeeElement(pmmUpgradePage.elements.updateNowButton);
I.switchTo('#grafana-iframe');
},

async verifyPreUpdateWidgetIsPresent(version) {
const locators = this.getLocators(version);

Expand All @@ -157,7 +181,17 @@ module.exports = {
'Available and Current versions match',
);
},
async verifyUpdateStatusIsPresent(expectedVersion) {
I.amOnPage(pmmUpgradePage.url);
I.switchTo();
I.waitForVisible(pmmUpgradePage.elements.newUpdateAvailable, 60);
I.waitForVisible(pmmUpgradePage.elements.runningVersion, 30);
I.waitForVisible(pmmUpgradePage.elements.newVersion, 30);

if (expectedVersion) {
I.see(expectedVersion, pmmUpgradePage.elements.newVersion);
}
},
async verifyPostUpdateWidgetIsPresent() {
const locators = this.getLocators('latest');

Expand Down Expand Up @@ -225,8 +259,14 @@ module.exports = {
},

async verifyPMMServerVersion(expectedVersion) {
I.waitForElement(this.fields.updateWidget.latest.currentVersion);
const actualVersion = await I.grabTextFrom(this.fields.updateWidget.latest.currentVersion);
I.amOnPage(pmmUpgradePage.url);
I.switchTo();
I.waitForVisible(pmmUpgradePage.elements.runningVersion, 60);
const actualVersion = await I.grabTextFrom(pmmUpgradePage.elements.runningVersion);

if (!expectedVersion) {
return;
}

I.assertContain(
actualVersion,
Expand Down
6 changes: 5 additions & 1 deletion codeceptjs-e2e/tests/pages/pmmUpgradePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ class PmmUpgradePage {
constructor() {
this.url = 'pmm-ui/updates';
this.elements = {
checkUpdatesNow: locate('button').withText('Check updates now'),
howToUpdateDocsLink: locate('a').withText('How to update docs'),
newUpdateAvailable: locate('h4').withText('New update available'),
newVersion: locate('strong').withText('New version:'),
runningVersion: locate('p').withText('Running version:'),
updateNowButton: locate('button').withText('Update now'),
checkUpdatesNow: locate('button').withText('Check Updates Now'),
updateSuccess: locate('p').withText('PMM Server installation complete!'),
};
}
Expand Down
13 changes: 10 additions & 3 deletions codeceptjs-e2e/tests/upgrade/upgradePMM_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,24 @@ Scenario(
'PMM-T288 - Verify user can see Update widget before upgrade [critical] @pmm-upgrade',
async ({ I, homePage }) => {
await I.stopMockingUpgrade();
I.amOnPage(homePage.url);
await homePage.verifyPreUpdateWidgetIsPresent(versionMinor);
await homePage.verifyUpdateStatusIsPresent(process.env.PMM_SERVER_LATEST);
},
);

Scenario(
'PMM-T3 - Verify user is able to Upgrade PMM version [blocker] @pmm-upgrade',
async ({ I, homePage }) => {
await I.stopMockingUpgrade();
I.amOnPage(homePage.url);

if (homePage.isGuiUpgradeRemoved()) {
await homePage.upgradePMMViaDocker(homePage.pmmServerName);
I.amOnPage(homePage.url);
await homePage.verifyPMMServerVersion(process.env.PMM_SERVER_LATEST);

return;
}

I.amOnPage(homePage.url);
await homePage.updatesModal.closeModal();
await homePage.upgradePMM(versionMinor);
},
Comment thread
travagliad marked this conversation as resolved.
Expand Down
2 changes: 2 additions & 0 deletions e2e_tests/helpers/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const apiEndpoints = {
readyz: '/v1/server/readyz',
settings: '/v1/server/settings',
updates: '**/v1/server/updates?force=**',
updatesStart: '/v1/server/updates:start',
updatesStatus: '/v1/server/updates:getStatus',
},
users: {
me: '**/v1/users/me',
Expand Down
9 changes: 7 additions & 2 deletions e2e_tests/pages/updates.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@ export interface UpdateInfo {
}

export default class UpdatesPage extends BasePage {
advancedSettingsUrl = '/pmm-ui/settings/advanced-settings';
clientsUrl = '/pmm-ui/updates/clients';
url = '/pmm-ui/updates';
homeUrl = '/pmm-ui/graph/';
builders = {};
buttons = {
updateNow: this.page.getByRole('button', { name: 'Update now' }),
howToUpdateDocs: this.page.getByRole('link', { name: 'How to update docs' }),
whatsNew: this.grafanaIframe().getByRole('link', { name: "What's new" }),
};
elements = {
availableSection: this.page.getByRole('heading', { name: /New update available/i }),
newVersionLine: this.page.locator('p').filter({ hasText: 'New version:' }),
checkForUpdates: this.page.getByText('Check for updates'),
newVersionLine: this.page.locator('strong').filter({ hasText: 'New version:' }),
pageTitle: this.page.getByRole('heading', { exact: true, name: 'Updates' }),
runningVersion: this.page.getByText('Running version:'),
updateNow: this.page.getByRole('button', { name: /update now/i }),
};
inputs = {};
messages = {};
Expand Down
Loading