Skip to content

Commit

Permalink
chore: add performance test script (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
junha-ahn authored Sep 21, 2023
1 parent 295a5c0 commit b0fbfad
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
.DS_Store

.env
*.pem

src/performanceTest/results/*
jacocoReport/

HELP.md
Expand Down
67 changes: 67 additions & 0 deletions src/performanceTest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# PerforanceTest

You can refer to the [k6 documentation](https://k6.io/) for additional information.

## Getting Started

This performance test runs on Docker Engine.

### 1. Start Docker Engine on your host

### 2. Copy the `bastion.pem` File

Copy the `bastion.pem` file to the `/src/performanceTest/initdb` directory.

This file is a public key used to access AWS RDS for database initialization.

### Configure Environment Variables

Set the required environment variables, or create a `.env` file within the `/src/performanceTest/` directory.

Here is an example `.env` file with placeholders:

```dotenv
MYSQL_HOST=
MYSQL_PORT=
MYSQL_USERNAME=
MYSQL_PASSWORD=
MYSQL_SCHEMA=
BASTION_HOST=
BASTION_USERNAME=
HOST=
# Monitoring Configuration (Optional)
GRAFANA_HOST=
K6_PROMETHEUS_RW_SERVER_URL=
K6_PROMETHEUS_RW_USERNAME=
K6_PROMETHEUS_RW_PASSWORD=
```

### 4. Run the following commands


Move to the `src/performanceTest` directory

You can change the `ENTRYPOINT` variable to run different test files:


```shell
# To run the `a_test.js` file
ENTRYPOINT=a_test.js docker-compose up

# To run the `b_test.js` file
ENTRYPOINT=b_test.js docker-compose up
```


## Running K6 Scripts Independently

If you want to run a K6 script **without Docker** and **without initializing the database**, you can use the following command:


```sh
# Install k6 on your host

HOST=http://... k6 run scripts/healthCheck.js
```
39 changes: 39 additions & 0 deletions src/performanceTest/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
version: '3'
services:
initdb:
build:
context: .
dockerfile: ./initdb/Dockerfile
volumes:
- ./initdb/initdb.sh:/initdb.sh
- ./initdb/sqls:/sqls
- ./initdb/bastion.pem:/bastion.pem
environment:
MYSQL_HOST: ${MYSQL_HOST}
MYSQL_PORT: ${MYSQL_PORT}
MYSQL_USERNAME: ${MYSQL_USERNAME}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_SCHEMA: ${MYSQL_SCHEMA}
BASTION_HOST: ${BASTION_HOST}
BASTION_USERNAME: ${BASTION_USERNAME}

k6:
depends_on:
initdb:
condition: service_completed_successfully
image: grafana/k6:latest
volumes:
- ./scripts:/scripts
- ./results:/results
environment:
HOST: ${HOST}
GRAFANA_HOST: ${GRAFANA_HOST}
K6_PROMETHEUS_RW_SERVER_URL: ${K6_PROMETHEUS_RW_SERVER_URL}
K6_PROMETHEUS_RW_USERNAME: ${K6_PROMETHEUS_RW_USERNAME}
K6_PROMETHEUS_RW_PASSWORD: ${K6_PROMETHEUS_RW_PASSWORD}
K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM: true
OUTPUT_HTML_DIR: "/results"
command: run /scripts/${ENTRYPOINT} -o experimental-prometheus-rw



9 changes: 9 additions & 0 deletions src/performanceTest/initdb/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM ubuntu:22.04

# 패키지 설치 및 스크립트 복사
RUN apt-get update -y
RUN apt-get install -y openssh-client mysql-client
# RUN chmod 600 /bastion.pem

# 스크립트 실행
CMD ["/bin/bash", "/initdb.sh"]
18 changes: 18 additions & 0 deletions src/performanceTest/initdb/initdb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

MYSQL_HOST="$MYSQL_HOST"
MYSQL_PORT="$MYSQL_PORT"
MYSQL_USERNAME="$MYSQL_USERNAME"
MYSQL_PASSWORD="$MYSQL_PASSWORD"
MYSQL_SCHEMA="$MYSQL_SCHEMA"
BASTION_HOST="$BASTION_HOST"
BASTION_USERNAME="$BASTION_USERNAME"
TUNNELING_PORT=30010

echo "Running initdb.sh"

chmod 600 /bastion.pem
ssh -i bastion.pem -CNf -L $TUNNELING_PORT:$MYSQL_HOST:$MYSQL_PORT $BASTION_USERNAME@$BASTION_HOST -o StrictHostKeyChecking=no

mysql -h 127.0.0.1 -P $TUNNELING_PORT -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -D $MYSQL_SCHEMA < /sqls/cleanup.sql
mysql -h 127.0.0.1 -P $TUNNELING_PORT -u $MYSQL_USERNAME -p$MYSQL_PASSWORD -D $MYSQL_SCHEMA < /sqls/initdata.sql
10 changes: 10 additions & 0 deletions src/performanceTest/initdb/sqls/cleanup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
USE ticketingdb;

SET foreign_key_checks = 0;

TRUNCATE user;
TRUNCATE event;
TRUNCATE bookmark;
TRUNCATE reservation;

SET foreign_key_checks = 1;
4 changes: 4 additions & 0 deletions src/performanceTest/initdb/sqls/initdata.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
USE ticketingdb;

INSERT INTO event (title, date, reservation_start_time, reservation_end_time, max_attendees, current_reservation_count) VALUES ('Concert', NOW(), NOW(), NOW(), 10, 0);
INSERT INTO event (title, date, reservation_start_time, reservation_end_time, max_attendees, current_reservation_count) VALUES ('Concert', NOW(), NOW(), NOW(), 10, 0);
17 changes: 17 additions & 0 deletions src/performanceTest/scripts/healthCheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { check } from "k6";
import Request from "./lib/request.js";
import hooks from "./lib/hooks.js";

export const handleSummary = hooks.handleSummary

export const options = {
vus: 10,
duration: '5s',
};

export default function () {
const req = new Request()
const res = req.helthCheck()
check(res, { "status == 200": (r) => r.status == 200 });
check(res, { "body == OK": (r) => r.body == "OK" });
}
13 changes: 13 additions & 0 deletions src/performanceTest/scripts/lib/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const removeLastSlash = (str) => {
if (str) {
return str[str.length - 1] == "/" ? str.slice(0, -1) : str
}
return str
}
export default {
START: new Date().valueOf(),
HOST: removeLastSlash(__ENV.HOST),
GRAFANA_HOST: removeLastSlash(__ENV.GRAFANA_HOST),
OUTPUT_HTML_DIR: __ENV.OUTPUT_HTML_DIR || './results',
DASHBOARD_DELAY: 1000 * 30 // 30 SEC
}
21 changes: 21 additions & 0 deletions src/performanceTest/scripts/lib/generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}

const getPrefix = () => {
return `K6-${uuid().substring(0, 10)}`;
}

const User = () => ({
name: `${getPrefix()}-name`,
email: `${getPrefix()}@email.com`,
password: `${getPrefix()}-password`,
});

export default {
User
}
3 changes: 3 additions & 0 deletions src/performanceTest/scripts/lib/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const getRandomByRange = (max) => Math.floor(Math.random() * max);

export const getOneFromList = (list) => list[getRandomByRange(list.length)];
38 changes: 38 additions & 0 deletions src/performanceTest/scripts/lib/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";
import { textSummary } from "https://jslib.k6.io/k6-summary/0.0.1/index.js";
import config from "./config.js";

export default {
handleSummary: function(data) {
const htmlPath = `${config.OUTPUT_HTML_DIR}/result.html`


let Dahboard = ''
if (config.GRAFANA_HOST) {
const timeRange = `from=${config.START - config.DASHBOARD_DELAY}&to=${new Date().valueOf() + config.DASHBOARD_DELAY}`
const query = `&orgId=1&refresh=10s&${timeRange}`
Dahboard = `
------------------------DASHBOARD----------------------------
HTML : ${htmlPath}
K6 : ${config.GRAFANA_HOST}/d/01npcT44k/official-k6-test-result?${query}
CLUSTER: ${config.GRAFANA_HOST}/d/85a562078cdf77779eaa1add43ccec1e/kubernetes-compute-resources-namespace-pods?${query}&var-datasource=default&var-cluster=&var-namespace=default
SPRING : ${config.GRAFANA_HOST}/d/OS7-NUiGz/spring-boot-statistics?${query}
MYSQL : ${config.GRAFANA_HOST}/d/549c2bf8936f7767ea6ac47c47b00f2a/mysql-exporter?${query}
-------------------------------------------------------------
`
}
return {
[htmlPath]: htmlReport(data),
stdout: `
${Dahboard}
${textSummary(data, { indent: " ", enableColors: true })}
`,
};
}
}
91 changes: 91 additions & 0 deletions src/performanceTest/scripts/lib/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import http from "k6/http";
import { sleep } from "k6";
import config from "./config.js";

export default class Request {
constructor(baseURL = config.HOST) {
this.baseURL = baseURL
this.Token = null
this.SLEEP = 2
}

beforeHook() {
// do nothing
}

afterHook() {
sleep(this.SLEEP)
}

setToken(token) {
if (token.substring(0, 7) === 'Bearer '){
// set this.token = token without 'Bearer '
this.Token = token.substring(7);
}
else {
this.Token = token;
}
}
getToken() {
return this.Token;
}

getHeaders() {
const headers = {
'Content-Type': 'application/json',
}
if (this.getToken() != null) {
headers['Authorization'] = `Bearer ${this.getToken()}`;
}
return headers
}

getParams() {
return {
headers: this.getHeaders(),
}
}


helthCheck() {
this.beforeHook()
const res= http.get(this.baseURL, this.getParams())
this.afterHook()
return res
}

getEvents() {
this.beforeHook()
const res = http.get(`${this.baseURL}/events/`);
this.afterHook()
return res
}
signup(body) {
this.beforeHook()
const res = http.post(`${this.baseURL}/users/signup`, JSON.stringify(body), this.getParams());
this.afterHook()
return res
}
signin(body) {
this.beforeHook()
const res = http.post(`${this.baseURL}/users/signin`, JSON.stringify({ email: body.email, password: body.password }), this.getParams());
this.setToken(res.json()['Authorization'])
this.afterHook()
return res
}
signout() {
this.setToken(null)
}
access_token_info() {
this.beforeHook()
const res = http.get(`${this.baseURL}/users/access_token_info`, this.getParams());
this.afterHook()
return res
}
createReservation(body) {
this.beforeHook()
const res = http.post(`${this.baseURL}/reservations`, JSON.stringify(body), this.getParams());
this.afterHook()
return res
}
}
22 changes: 22 additions & 0 deletions src/performanceTest/scripts/reservationCheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { check } from "k6";
import Request from "./lib/request.js";
import generator from "./lib/generator.js";
import hooks from "./lib/hooks.js";
import { getOneFromList } from "./lib/helpers.js";


export const handleSummary = hooks.handleSummary

export default function () {
const req = new Request()

const user = generator.User()
req.signup(user)
req.signin(user)

const event = getOneFromList(req.getEvents().json())

const res = req.createReservation({ eventId: event.id })
check(res, { "status == 200": (r) => r.status == 200 });
check(res, { "created.eventId == event.id": (r) => r.json().eventId == event.id });
}
Loading

0 comments on commit b0fbfad

Please sign in to comment.