Skip to content
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 load tester for yamcs to simulate open mct traffic #390

Closed
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions tests/k6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Installation
To run the K6 load testing tools, you'll need to:
* [Install K6](https://k6.io/docs/get-started/installation/)
* Install [YAMCS Quickstart](https://github.com/yamcs/quickstart)

# Running
* Start [YAMCS](https://github.com/yamcs/quickstart#running-yamcs)
* Start the [simulator](https://github.com/yamcs/quickstart#telemetry) in YAMCS Quickstart
* Run the K6 script:
```sh
export OPENMCT_USERNAME=testuser;
export OPENMCT_PASSWORD=NasaIsCool!
k6 run websocket-subscriptions.js
k6 run full-browser-test.js
```
93 changes: 93 additions & 0 deletions tests/k6/full-browser-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* global __VU __ENV */

import { sleep, check } from 'k6';
import { browser } from 'k6/experimental/browser';
import { Counter } from 'k6/metrics';
import { b64encode } from 'k6/encoding';

const maxClients = 30;
const dwellTimeInMs = 500000;
const baseURL = 'http://localhost:8040';
const domainObject = 'ae93d303-e2c1-4514-9dbf-e4822787b058';
const browserURL = `${baseURL}/#/browse/mine/${domainObject}`;

// Get the username and password from environment variables
const openmctUsername = __ENV.OPENMCT_USERNAME;
const openmctPassword = __ENV.OPENMCT_PASSWORD;

export const options = {
scenarios: {
'ui': {
executor: 'ramping-vus',
options: {
browser: {
type: 'chromium'
}
},
startVUs: 0,
stages: [
{
duration: '1s',
target: maxClients - 25
},
{
duration: '30s',
target: maxClients - 15
},
{
duration: '1m',
target: maxClients - 10
},
{
duration: '2m',
target: maxClients - 5
},
{
duration: '3m',
target: maxClients
}
],
gracefulRampDown: '30s'
}
},
browser: {
type: 'chromium'
}
};

function createBasicAuthHeader(username, password) {
const base64Credentials = b64encode(`${username}:${password}`);

return `Basic ${base64Credentials}`;
}

export const browserCounter = new Counter('Browser Instances');
const basicAuthHeader = createBasicAuthHeader(openmctUsername, openmctPassword);

export async function browserScenario() {
const page = browser.newPage();
page.setExtraHTTPHeaders({
'Authorization': basicAuthHeader
});
try {
console.info(`🟢 Starting browser ${__VU} to connect to Open MCT ${browserURL}`);
await page.goto(browserURL);
browserCounter.add(1);
check(page, {
'header': pageToCheck => pageToCheck.locator('.c-button__label').textContent() === 'Create'
});
sleep(dwellTimeInMs);
} finally {
page.close();
browserCounter.add(-1);
console.info(`✋ Stopping browser ${__VU}`);
}
}

export function teardown() {
console.info(`✅ Browser instances opened during the test: ${browserCounter.value}`);
}

export default async () => {
await browserScenario();
};
106 changes: 106 additions & 0 deletions tests/k6/websocket-subscriptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* global __VU __ENV */

import { WebSocket } from 'k6/experimental/websockets';
import { sleep } from 'k6';
import { b64encode } from 'k6/encoding';

const maxClients = 60;
const workersPerClient = 30;
const testingDuration = '1h';
const yamcsURL = `ws://localhost:8040/yamcs/api/websocket`;
const digestionTimeInMs = 500;

// Get the username and password from environment variables
const openmctUsername = __ENV.OPENMCT_USERNAME;
const openmctPassword = __ENV.OPENMCT_PASSWORD;
const basicAuthHeader = createBasicAuthHeader(openmctUsername, openmctPassword);

export const options = {
vus: maxClients,
scenarios: {
contacts: {
executor: 'ramping-vus',
startVUs: 1,
stages: [
{
duration: '30s',
target: maxClients - 20
},
{
duration: '30s',
target: maxClients - 15
},
{
duration: '30s',
target: maxClients - 10
},
{
duration: '30s',
target: maxClients - 5
},
{
duration: testingDuration,
target: maxClients
}
],
gracefulRampDown: '0s'
}
}
};

function createBasicAuthHeader(username, password) {
const base64Credentials = b64encode(`${username}:${password}`);

return `Basic ${base64Credentials}`;
}

function startYamcsWsWorker(id) {
// make array of x,y,z
const parameterTypes = ['x', 'y', 'z'];
// pick one at random
const randomParameter = parameterTypes[Math.floor(Math.random() * parameterTypes.length)];
const parameterName = `/myproject/Gyro.${randomParameter}`;
console.info(`📨 Starting YAMCS websocket load test for ${id} to ${yamcsURL} subscribing to ${parameterName}`);
const websocket = new WebSocket(yamcsURL, { headers: { Authorization: basicAuthHeader } });
websocket.onerror = (e) => {
console.error(`🚨 Websocket error`, e);
};

websocket.addEventListener('open', () => {
console.log(`Client id ${id}: connected`);
console.info(`🔌 Established websocket connection to ${yamcsURL}`);
websocket.addEventListener('message', (rawMessage) => {
console.info(`📫 Client ${id} received YAMCS message for ${parameterName} at time ${new Date().toISOString()}, swallowing in ${digestionTimeInMs}ms`);
// block function while we digest the message
sleep(digestionTimeInMs / 1000);
console.info(`📭 Client ${id} finished digesting message for ${parameterName} at time ${new Date().toISOString()}`);
});

websocket.addEventListener('error', (error) => {
console.error(`🚨 Client ${id} got websocket error`, error);
});

websocket.addEventListener('close', () => {
console.warn(`🚪 Client ${id} websocket closed.`);
});

// send subscription request message
websocket.send(JSON.stringify({
type: 'parameters',
id,
options: {
instance: 'myproject',
processor: 'realtime',
id: [{name: parameterName}],
sendFromCache: true,
updateOnExpiration: true
}
}));
});
}

export default () => {
for (let i = 0; i < workersPerClient; i++) {
startYamcsWsWorker(`${__VU}`);
}
};