Skip to content

Commit

Permalink
feat: checking cluster and app state during ci (#275)
Browse files Browse the repository at this point in the history
access kots through web and make sure the app is ready and the cluster
is up to date.
  • Loading branch information
ricardomaraschini authored Jan 17, 2024
1 parent b1d91dd commit e723f32
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 0 deletions.
31 changes: 31 additions & 0 deletions e2e/install_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package e2e

import (
"encoding/json"
"testing"

"github.com/replicatedhq/embedded-cluster/e2e/cluster"
Expand Down Expand Up @@ -30,6 +31,36 @@ func TestSingleNodeInstallation(t *testing.T) {
if _, _, err := RunCommandOnNode(t, tc, 0, line); err != nil {
t.Fatalf("fail to install embedded-cluster on node %s: %v", tc.Nodes[0], err)
}
t.Log("installing puppeteer on node 0")
line = []string{"install-puppeteer.sh"}
if stdout, stderr, err := RunCommandOnNode(t, tc, 0, line); err != nil {
t.Log("stdout:", stdout)
t.Log("stderr:", stderr)
t.Fatalf("fail to install puppeteer on node %s: %v", tc.Nodes[0], err)
}
t.Log("accessing kotsadm interface and checking app and cluster state")
line = []string{"puppeteer.sh", "check-app-and-cluster-status.js", "10.0.0.2"}
stdout, stderr, err := RunCommandOnNode(t, tc, 0, line)
if err != nil {
t.Log("stdout:", stdout)
t.Log("stderr:", stderr)
t.Fatalf("fail to access kotsadm interface and state: %v", err)
}
type response struct {
App string `json:"app"`
Cluster string `json:"cluster"`
}
var r response
if err := json.Unmarshal([]byte(stdout), &r); err != nil {
t.Log("stdout:", stdout)
t.Log("stderr:", stderr)
t.Fatalf("fail to parse script response: %v", err)
}
if r.App != "Ready" || r.Cluster != "Up to date" {
t.Log("stdout:", stdout)
t.Log("stderr:", stderr)
t.Fatalf("cluster or app not ready: %s", stdout)
}
}

func TestSingleNodeInstallationRockyLinux8(t *testing.T) {
Expand Down
230 changes: 230 additions & 0 deletions e2e/scripts/check-app-and-cluster-status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
#!/usr/bin/env node

/*
* this script has been generated with chrome recorder and then pasted here.
* some parts were manually changed, these are flagged with a CUSTOM comment.
* all logging has also been manually added (process.stderr.write() calls).
* this script is meant to be run as an argument to the `puppeteer.sh` script.
*/

const puppeteer = require('puppeteer'); // v20.7.4 or later

(async () => {
const browser = await puppeteer.launch(
{
headless: 'new',
// CUSTOM: added the following line to fix the "No usable sandbox!" error.
args: ['--no-sandbox', '--disable-setuid-sandbox']
}
);
const page = await browser.newPage();
const timeout = 5000;
page.setDefaultTimeout(timeout);
const args = process.argv.slice(2);
if (args.length !== 1) {
throw new Error('usage: check-app-and-cluster-status.js <kotsadm-ip>');
}

{
const targetPage = page;
await targetPage.setViewport({
width: 1920,
height: 934
})
}
{
process.stderr.write("opening a new tab\n");
const targetPage = page;
const promises = [];
const startWaitingForEvents = () => {
promises.push(targetPage.waitForNavigation());
}
startWaitingForEvents();
await targetPage.goto('chrome://new-tab-page/');
await Promise.all(promises);
}
{
process.stderr.write("acessing kotsadm on port 30000\n");
const targetPage = page;
const promises = [];
const startWaitingForEvents = () => {
promises.push(targetPage.waitForNavigation());
}
startWaitingForEvents();
// CUSTOM: using the command line argument.
await targetPage.goto(`http://${args[0]}:30000/`);
await Promise.all(promises);
}
{
process.stderr.write("waiting and clickin on the 'Continue to Setup' button\n");
const targetPage = page;
const promises = [];
const startWaitingForEvents = () => {
promises.push(targetPage.waitForNavigation());
}
await puppeteer.Locator.race([
targetPage.locator('::-p-aria(Continue to Setup)'),
targetPage.locator('button'),
targetPage.locator('::-p-xpath(/html/body/div/div/div[2]/div[1]/div[4]/button)'),
targetPage.locator(':scope >>> button'),
targetPage.locator('::-p-text(Continue to Setup)')
])
.setTimeout(timeout)
.on('action', () => startWaitingForEvents())
.click({
offset: {
x: 44,
y: 15,
},
});
await Promise.all(promises);
}
{
process.stderr.write("waiting and clicking on 'Advanced' to move on with the certificate\n");
const targetPage = page;
await puppeteer.Locator.race([
targetPage.locator('::-p-aria(Advanced)'),
targetPage.locator('#details-button'),
targetPage.locator('::-p-xpath(//*[@id=\\"details-button\\"])'),
targetPage.locator(':scope >>> #details-button'),
targetPage.locator('::-p-text(Advanced)')
])
.setTimeout(timeout)
.click({
offset: {
x: 77,
y: 21.2421875,
},
});
}
{
process.stderr.write("waiting and clicking on 'Proceed' to move on with the certificate\n");
const targetPage = page;
// CUSTOM: using command line argument.
await puppeteer.Locator.race([
targetPage.locator(`::-p-aria(Proceed to ${args[0]} \\(unsafe\\))`),
targetPage.locator('#proceed-link'),
targetPage.locator('::-p-xpath(//*[@id=\\"proceed-link\\"])'),
targetPage.locator(':scope >>> #proceed-link'),
targetPage.locator(`::-p-text(Proceed to ${args[0]})`)
])
.setTimeout(timeout)
.click({
offset: {
x: 48,
y: 7.7421875,
},
});
}
{
process.stderr.write("going to the /tls endpoint\n");
const targetPage = page;
const promises = [];
const startWaitingForEvents = () => {
promises.push(targetPage.waitForNavigation());
}
startWaitingForEvents();
// CUSTOM: using the command line argument.
await targetPage.goto(`https://${args[0]}:30000/tls`);
await Promise.all(promises);
}
{
process.stderr.write("waiting and clicking on 'Continue'\n");
const targetPage = page;
const promises = [];
const startWaitingForEvents = () => {
promises.push(targetPage.waitForNavigation());
}
await puppeteer.Locator.race([
targetPage.locator('::-p-aria(Continue)'),
targetPage.locator('button'),
targetPage.locator('::-p-xpath(//*[@id=\\"upload-form\\"]/div[6]/button)'),
targetPage.locator(':scope >>> button'),
targetPage.locator('::-p-text(Continue\n )')
])
.setTimeout(timeout)
.on('action', () => startWaitingForEvents())
.click({
offset: {
x: 45,
y: 6,
},
});
await Promise.all(promises);
}
{
process.stderr.write("waiting and clicking in the password field\n");
const targetPage = page;
await puppeteer.Locator.race([
targetPage.locator('::-p-aria(password)'),
targetPage.locator('input'),
targetPage.locator('::-p-xpath(//*[@id=\\"app\\"]/div/div[2]/div/div/div/div[2]/div/div/div[1]/input)'),
targetPage.locator(':scope >>> input')
])
.setTimeout(timeout)
.click({
offset: {
x: 35,
y: 17.5078125,
},
});
}
{
process.stderr.write("typing the password\n");
const targetPage = page;
await puppeteer.Locator.race([
targetPage.locator('::-p-aria(password)'),
targetPage.locator('input'),
targetPage.locator('::-p-xpath(//*[@id=\\"app\\"]/div/div[2]/div/div/div/div[2]/div/div/div[1]/input)'),
targetPage.locator(':scope >>> input')
])
.setTimeout(timeout)
.fill('password');
}
{
process.stderr.write("clicking in the Log in button\n");
const targetPage = page;
await puppeteer.Locator.race([
targetPage.locator('::-p-aria(Log in)'),
targetPage.locator('button'),
targetPage.locator('::-p-xpath(//*[@id=\\"app\\"]/div/div[2]/div/div/div/div[2]/div/div/div[2]/button)'),
targetPage.locator(':scope >>> button')
])
.setTimeout(timeout)
.click({
offset: {
x: 27,
y: 22.5078125,
},
});
}
{
// CUSTOM: finding the element with the app state and extracting its content.
let state = {app: "", cluster:""};
process.stderr.write("waiting and fetching the application and cluster state\n");
const targetPage = page;
await targetPage.waitForSelector('#app > div > div.flex1.flex-column.u-overflow--auto.tw-relative > div > div > div > div.flex-column.flex1.u-position--relative.u-overflow--auto.u-padding--20 > div > div > div.flex.flex1.alignItems--center > div.u-marginLeft--20 > div > div:nth-child(1) > span:nth-child(5)');
let elementContent = await targetPage.evaluate(() => {
const element = document.querySelector('#app > div > div.flex1.flex-column.u-overflow--auto.tw-relative > div > div > div > div.flex-column.flex1.u-position--relative.u-overflow--auto.u-padding--20 > div > div > div.flex.flex1.alignItems--center > div.u-marginLeft--20 > div > div:nth-child(1) > span:nth-child(5)');
return element ? element.textContent : null;
});
if (elementContent) {
state.cluster = elementContent;
}
await targetPage.waitForSelector('#app > div > div.flex1.flex-column.u-overflow--auto.tw-relative > div > div > div > div.flex-column.flex1.u-position--relative.u-overflow--auto.u-padding--20 > div > div > div.flex.flex1.alignItems--center > div.u-marginLeft--20 > div > div:nth-child(1) > span:nth-child(2)');
elementContent = await targetPage.evaluate(() => {
const element = document.querySelector('#app > div > div.flex1.flex-column.u-overflow--auto.tw-relative > div > div > div > div.flex-column.flex1.u-position--relative.u-overflow--auto.u-padding--20 > div > div > div.flex.flex1.alignItems--center > div.u-marginLeft--20 > div > div:nth-child(1) > span:nth-child(2)');
return element ? element.textContent : null;
});
if (elementContent) {
state.app = elementContent;
}
console.log(JSON.stringify(state));
}

await browser.close();

})().catch(err => {
console.error(err);
process.exit(1);
});
47 changes: 47 additions & 0 deletions e2e/scripts/install-puppeteer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euox pipefail

apt-get update -y
apt-get install -y \
ca-certificates \
curl \
gnupg \
chromium-browser \
libx11-xcb1 \
libxcomposite1 \
libasound2 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
libgbm1 \
libgcc1 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libstdc++6 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxi6 \
libxrandr2 \
libxrender1 \
libxss1 \
libnss3 \
libxtst6

curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
apt-get update && apt-get install nodejs -y
npm install -g puppeteer
3 changes: 3 additions & 0 deletions e2e/scripts/puppeteer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
NODE_PATH="$(npm root -g)" "$@"

0 comments on commit e723f32

Please sign in to comment.