Skip to content

Commit

Permalink
Merge pull request #117 from bcgov/chore/k6-load-test
Browse files Browse the repository at this point in the history
Test rate limit with k6
  • Loading branch information
TimCsaky authored Feb 7, 2025
2 parents 757819f + e58c0f1 commit 9dae1ab
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .github/environments/values.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,13 @@ config:
UPLOAD_FILE_COUNT: "1"
UPLOAD_FILE_SIZE: 25MB

persistentVolumeClaim:
# -- Specifies whether a persistent volume claim should be created
enabled: true
# -- Default storage class type
storageClassName: netapp-file-standard
# -- PVC Storage size (use M or G, not Mi or Gi)
storageSize: 5G

fluentBit:
enabled: true
8 changes: 8 additions & 0 deletions .github/environments/values.prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,13 @@ config:
UPLOAD_FILE_COUNT: "1"
UPLOAD_FILE_SIZE: 25MB

persistentVolumeClaim:
# -- Specifies whether a persistent volume claim should be created
enabled: true
# -- Default storage class type
storageClassName: netapp-file-standard
# -- PVC Storage size (use M or G, not Mi or Gi)
storageSize: 5G

fluentBit:
enabled: true
8 changes: 8 additions & 0 deletions .github/environments/values.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,13 @@ config:
UPLOAD_FILE_COUNT: "1"
UPLOAD_FILE_SIZE: 25MB

persistentVolumeClaim:
# -- Specifies whether a persistent volume claim should be created
enabled: true
# -- Default storage class type
storageClassName: netapp-file-standard
# -- PVC Storage size (use M or G, not Mi or Gi)
storageSize: 5G

fluentBit:
enabled: true
22 changes: 22 additions & 0 deletions k6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Load testing with K6

[K6](https://k6.io/docs/) is a load testing tool.
Using the K6 command line interface, you can run the scripts found in this directory to test the performance of CDOGS API features.

Note: It is important to not run load tests against production environments. Always check with your server administrators before load testing in a shared server environment.

## Prerequesites

The simple test scripts (for example: [templating.js](templating.js) can be updated with actual values specific to your envionment (for example: your CDOGS api url and authorization token) or could also pass these values using parameters of the K6 command used to trigger the test. See more K6 details on how [Environment Variables](https://k6.io/docs/using-k6/environment-variables/) work.

### Running the tests

```sh
k6 run -e API_PATH=http://cdogs-dev.api.gov.bc.ca/api/v2 -e AUTH_TOKEN=InsertJwtHere templating.js
```

To enable logging, add `--log-output=file=./output.json --log-format=json`. At the moment, the tests currently only log the HTTP response code.

By default, the tests will make 200 evenly-spaced requests within 1 minute. To increase the number of requests the tests will make, add `-e RATE=x` (`x` is a multiplier that gets applied against the rate limit being tested against).

To change the rate limit being tested against, add `-e RATE_LIMIT=300`. By default, this value is `200`.
90 changes: 90 additions & 0 deletions k6/sample_contexts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"isaDate": "20191127",
"notes": "This is a long bit of text that is eventually broken up by some newlines and/or newlines/carriageReturns. \n So that means that the json can have slash-n, slash-r in it and the template will treat that as line returns in a plain text representation, or in the case of DOCX and ODT will replace it with the appropriate document-type XML line break tag. Here's a slash-n: \n Now here's a slash-r slash-n \r\n And another set of line breaks here\n\n\n See the Carbone documentation here https://carbone.io/documentation.html#formatters and look for convCRLF() \r\n ",
"admins": [
{
"party": "X",
"name": "John Smith",
"phone": "555-123-4567",
"fax": "555-098-6543",
"email": "fakeemail@email.com"
},
{
"party": "Y",
"name": "Anna Johnson",
"phone": "555-333-4444",
"fax": "555-111-2222",
"email": "anotheremail@email.com"
}
],
"otherParties": [
{
"name": "Lucas O'Neil"
},
{
"name": "Matthew Hall"
},
{
"name": "Jeremy"
},
{
"name": "Jason"
},
{
"name": "Bill"
},
{
"name": "Ted"
},
{
"name": "Excellent Adventure"
}
],
"offices": [
{
"office": "Victoria",
"applications": [
{
"name": "Mines",
"contact": "Jane Smith"
},
{
"name": "Water",
"contact": "Bob Bobby"
},
{
"name": "Forests",
"contact": "Alan"
},
{
"name": "Roads",
"contact": "Harvey"
}
]
},
{
"office": "Kamloops",
"applications": [
{
"name": "Licencing",
"contact": "Jane Smith"
},
{
"name": "Rejections",
"contact": "Bob Bobby"
}
]
},
{
"office": "Vancouver",
"applications": [
{
"name": "Secret Application"
},
{
"name": "Mountains"
}
]
}
]
}
1 change: 1 addition & 0 deletions k6/sample_template.txt

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions k6/templating.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import http from 'k6/http';
import { check, sleep } from 'k6';
import encoding from 'k6/encoding';

// -------------------------------------------------------------------------------------------------
// Init
// -------------------------------------------------------------------------------------------------
// https://k6.io/docs/using-k6/environment-variables

const apiPath = `${__ENV.API_PATH}`; // include "/api/v2"
const authToken = `${__ENV.AUTH_TOKEN}`; // exchange token elsewhere, then pass JWT here
const multiplier = parseInt(`${__ENV.RATE}`) ?? 4; // change multiplier to run test faster
const RATE_LIMIT_PER_MINUTE = parseInt(`${__ENV.RATE_LIMIT}`) ?? 200;

// k6 options (https://k6.io/docs/using-k6/k6-options/)
export const options = {
scenarios: {
rateLimitTest: {
executor: 'constant-arrival-rate',
rate: RATE_LIMIT_PER_MINUTE * multiplier, // requests to make per minute
duration: '1m', // duration must be <5m due to JWT expiry
preAllocatedVUs: 10,
timeUnit: '1m',
maxVUs: 100,
},
},
};

const url = `${apiPath}/template/render`;

const headers = {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
};

const body = {
// Data File for template_information_sharing_agreement.docx from DGRSC
data: JSON.parse('sample_contexts.json'),
options: {
reportName: 'information_sharing_agreement',
convertTo: 'pdf',
overwrite: true
},
template: {
// template_information_sharing_agreement.docx from DGRSC
content: open('sample_template.txt'),
encodingType: 'base64',
fileType: 'docx'
}
}

// run k6
export default function () {

// make the http request
const res = http.post(url, JSON.stringify(body), {headers: headers});

// To enable logging: --log-output=file=./output.json --log-format=json
console.log(res.status);

// tests
// rate limit headers: https://docs.konghq.com/hub/kong-inc/rate-limiting/#headers-sent-to-the-client
check(res, {
'is status 200 or 429': (r) => r.status === 200 || r.status === 429,
'is returning the correct templated response': (r) => r.body == `Hello ${body.data.firstName} ${body.data.lastName}!`,
'is returning the correct RateLimit-Limit header': (r) => r.headers['Ratelimit-Limit'] == RATE_LIMIT_PER_MINUTE,
'is returning the correct RateLimit-Remaining header': (r) => r.headers['Ratelimit-Remaining'] < RATE_LIMIT_PER_MINUTE,
'is returning the correct X-RateLimit-Limit-Minute header': (r) => r.headers['X-Ratelimit-Limit-Minute'] == RATE_LIMIT_PER_MINUTE,
'is returning the correct X-RateLimit-Remaining-Minute header': (r) => r.headers['X-Ratelimit-Remaining-Minute'] < RATE_LIMIT_PER_MINUTE,
});

}

0 comments on commit 9dae1ab

Please sign in to comment.