Make load test script more self-documenting

This adds a script to package.json that will guide the user on e.g.
what env vars to set when they aren't set.

It also allows you to pass in a plain email address and have the
performance test send breach notifications for that specific email
address, without having to manually calculate a hash.
This commit is contained in:
Vincent 2024-09-16 14:49:44 +02:00 коммит произвёл Vincent
Родитель 45c1a2b8b2
Коммит 7667ecbd51
4 изменённых файлов: 59 добавлений и 18 удалений

Просмотреть файл

@ -219,9 +219,18 @@ To test the HIBP breach alerts endpoint, use:
```sh
export SERVER_URL=...
export HIBP_NOTIFY_TOKEN=...
k6 -u 10 src/scripts/loadtest/hibp.js # Run with 10 virtual users
npm run loadtest:hbibp-webhook
```
You can customise the number of requests to send in parallel ("virtual users") by setting the
[`K6_VUS`](https://grafana.com/docs/k6/latest/using-k6/k6-options/reference/#vus) environment
variable (default 1000), and for how long to send those requests by setting the
[`K6_DURATION`](https://grafana.com/docs/k6/latest/using-k6/k6-options/reference/#duration)
environment variable (default 30s).
You can also enforce the alert being sent for a specific email address via the
`LOADTEST_BREACHED_EMAIL` environment variable.
See https://grafana.com/docs/k6/latest/get-started/running-k6/ for more information.
## Localization

Просмотреть файл

@ -49,6 +49,7 @@
"build-glean-docs": "npm run build-glean-frontend-docs && npm run build-glean-backend-docs",
"build-glean-frontend-docs": "glean translate src/telemetry/metrics.yaml --format markdown --output docs/telemetry/frontent",
"build-glean-backend-docs": "glean translate src/telemetry/backend-metrics.yaml --format markdown --output docs/telemetry/backend",
"loadtest:hbibp-webhook": "echo 'Ensure k6 is installed; see:\n\thttps://grafana.com/docs/k6/latest/set-up/install-k6/\n' && k6 run src/scripts/loadtest/hibp.js",
"validate-nimbus": "sh src/scripts/build/validate-nimbus-file.sh"
},
"repository": {

Просмотреть файл

@ -14,6 +14,12 @@ const projectId = process.env.GCP_PUBSUB_PROJECT_ID;
const topicName = process.env.GCP_PUBSUB_TOPIC_NAME;
const subscriptionName = process.env.GCP_PUBSUB_SUBSCRIPTION_NAME;
export type PostHibpNotificationRequestBody = {
breachName: string;
hashPrefix: string;
hashSuffixes: string[];
};
/**
* Whenever a breach is detected on the HIBP side, HIBP sends a request to this endpoint.
* The payload is checked for validity, and immediately queued if it is valid.
@ -21,8 +27,8 @@ const subscriptionName = process.env.GCP_PUBSUB_SUBSCRIPTION_NAME;
* @param req
*/
export async function POST(req: NextRequest) {
let pubsub;
let json;
let pubsub: PubSub;
let json: PostHibpNotificationRequestBody;
try {
if (!projectId) {
@ -37,7 +43,7 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ success: false }, { status: 401 });
}
json = await req.json();
json = (await req.json()) as PostHibpNotificationRequestBody;
if (!(json.breachName && json.hashPrefix && json.hashSuffixes)) {
logger.error(

Просмотреть файл

@ -11,33 +11,57 @@
* @see https://grafana.com/docs/k6/latest/get-started/running-k6
*/
/* eslint-disable import/no-unresolved */
/* eslint-disable no-undef */
import { post } from "k6/http";
import crypto from "k6/crypto";
const url = `${__ENV.SERVER_URL}/api/v1/hibp/notify`;
// k6 exposes environment variables via the __ENV variable:
// https://grafana.com/docs/k6/latest/using-k6/environment-variables/
/** @type {typeof process.env} */
const envVars = __ENV;
// eslint-disable-next-line import/no-anonymous-default-export
export default function () {
const HIBP_NOTIFY_TOKEN = envVars.HIBP_NOTIFY_TOKEN;
if (typeof HIBP_NOTIFY_TOKEN !== "string") {
throw new Error(
"Make sure to set the following environment variable: HIBP_NOTIFY_TOKEN",
);
}
const VIRTUAL_USERS =
typeof envVars.K6_VIRTUAL_USERS === "string"
? parseInt(envVars.K6_VIRTUAL_USERS, 10)
: typeof envVars.K6_VUS === "string"
? parseInt(envVars.K6_VUS, 10)
: 1000;
const DURATION =
typeof envVars.K6_DURATION === "string" ? envVars.K6_DURATION : "30s";
export const options = {
vus: VIRTUAL_USERS,
duration: DURATION,
};
const url = `${envVars.SERVER_URL ?? "https://stage.firefoxmonitor.nonprod.cloudops.mozgcp.net"}/api/v1/hibp/notify`;
const mockedBreachedEmailHash =
typeof envVars.LOADTEST_BREACHED_EMAIL === "string"
? crypto.sha1(envVars.LOADTEST_BREACHED_EMAIL, "hex")
: "1c48923da9f6f17165711712d11bc104087444cc";
export const run = () => {
/** @type {import("../../app/api/v1/hibp/notify/route").PostHibpNotificationRequestBody} */
let data = {
breachName: "ApexSMS",
// NOTE: modify this hash range if you want to receive email to specific test account(s).
// This example should only email an address that is a sha1 hash for 1c48923da9f6f17165711712d11bc104087444cc.
// See https://www.troyhunt.com/understanding-have-i-been-pwneds-use-of-sha-1-and-k-anonymity/ for more information.
hashPrefix: "1c4892",
hashSuffixes: [
"3da9f6f17165711712d11bc104087444cc",
"3da9f6fffff5711712d11bc104087444cc",
],
hashPrefix: mockedBreachedEmailHash.substring(0, 6),
hashSuffixes: [mockedBreachedEmailHash.substring(6)],
};
// Using a JSON string as body
let res = post(url, JSON.stringify(data), {
headers: {
"Content-Type": "application/json",
// eslint-disable-next-line no-undef
Authorization: `Bearer ${__ENV.HIBP_NOTIFY_TOKEN}`,
Authorization: `Bearer ${HIBP_NOTIFY_TOKEN}`,
},
});
@ -49,4 +73,5 @@ export default function () {
} catch (ex) {
throw new Error(`Failed to parse result: ${res.status}, ${res.text}`);
}
}
};
export default run;