commit
24f045b3ab
5 changed files with 1587 additions and 0 deletions
@ -0,0 +1,57 @@ |
|||||
|
# K6 scripts to run load tests against Keycloak |
||||
|
|
||||
|
## Setup |
||||
|
|
||||
|
```sh |
||||
|
sudo wget https://bintray.com/loadimpact/rpm/rpm -O /etc/yum.repos.d/bintray-loadimpact-rpm.repo |
||||
|
sudo yum install -y k6 |
||||
|
``` |
||||
|
|
||||
|
**/tmp/statsd_exporter.yaml**: |
||||
|
|
||||
|
```yaml |
||||
|
defaults: |
||||
|
observer_type: histogram |
||||
|
mappings: |
||||
|
- match: "k6.*" |
||||
|
name: "k6_${1}" |
||||
|
- match: "k6.check.*.*.*" |
||||
|
name: "k6_check" |
||||
|
labels: |
||||
|
http_name: "$1" |
||||
|
check_name: "$2" |
||||
|
outcome: "$3" |
||||
|
``` |
||||
|
|
||||
|
```sh |
||||
|
sudo podman run -d --name statsd_exporter -p 9102:9102 -p 8125:8125/udp -v /tmp/statsd_exporter.yaml:/etc/statsd_exporter.yaml quay.io/prometheus/statsd-exporter:latest --statsd.listen-udp=:8125 --statsd.mapping-config=/etc/statsd_exporter.yaml |
||||
|
``` |
||||
|
|
||||
|
**/tmp/prometheus.yaml**: |
||||
|
|
||||
|
```yaml |
||||
|
global: |
||||
|
scrape_interval: 1s |
||||
|
evaluation_interval: 1s |
||||
|
|
||||
|
scrape_configs: |
||||
|
- job_name: 'statsd_exporter' |
||||
|
static_configs: |
||||
|
- targets: ['statsd_exporter.dns.podman:9102'] |
||||
|
labels: {} |
||||
|
metric_relabel_configs: |
||||
|
- regex: '(job|instance)' |
||||
|
action: labeldrop |
||||
|
``` |
||||
|
|
||||
|
```sh |
||||
|
sudo podman run -d --name prometheus -p 9090:9090 -v /tmp/prometheus.yaml:/etc/prometheus/prometheus.yml prom/prometheus |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
```sh |
||||
|
export K6_DATADOG_TAG_BLACKLIST="url" |
||||
|
k6 run -o datadog login.js |
||||
|
``` |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
* |
||||
File diff suppressed because it is too large
@ -0,0 +1,53 @@ |
|||||
|
import { Rate } from "k6/metrics"; |
||||
|
|
||||
|
export function pickRealm(realmCount) { |
||||
|
var realmId = __VU % realmCount; |
||||
|
realmId = `${realmId}`.padStart(3, "0"); |
||||
|
var fileName = `data/realm-${realmId}.json`; |
||||
|
return JSON.parse(open(fileName)); |
||||
|
} |
||||
|
|
||||
|
export function pickClient(realm) { |
||||
|
var clients = realm.clients; |
||||
|
if (clients == null || clients.length == 0) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var i = Math.floor(Math.random() * Math.floor(clients.length)); |
||||
|
return clients[i]; |
||||
|
} |
||||
|
|
||||
|
export function pickUser(realm) { |
||||
|
var users = realm.users; |
||||
|
if (users == null || users.length == 0) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var i = Math.floor(Math.random() * Math.floor(users.length)); |
||||
|
return users[i]; |
||||
|
} |
||||
|
|
||||
|
export var script_errors = Rate("script_errors"); |
||||
|
export function wrapWithErrorCounting(fn) { |
||||
|
return () => { |
||||
|
try { |
||||
|
fn(); |
||||
|
script_errors.add(0); |
||||
|
} catch (e) { |
||||
|
script_errors.add(1); |
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function buildQueryString(data) { |
||||
|
const result = []; |
||||
|
|
||||
|
Object.keys(data) |
||||
|
.forEach((key) => { |
||||
|
const encode = encodeURIComponent; |
||||
|
result.push(encode(key) + "=" + encode(data[key])); |
||||
|
}); |
||||
|
|
||||
|
return result.join("&"); |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
import http from 'k6/http'; |
||||
|
import { check, group, sleep } from 'k6'; |
||||
|
import { uuidv4 } from "https://jslib.k6.io/k6-utils/1.0.0/index.js"; |
||||
|
import { pickRealm, pickClient, pickUser, wrapWithErrorCounting, buildQueryString } from "./lib/keycloak.js"; |
||||
|
import { randomSeed } from 'k6'; |
||||
|
|
||||
|
export let options = { |
||||
|
stages: [ |
||||
|
{ duration: "20s", target: 5 }, |
||||
|
{ duration: "1m", target: 100 } |
||||
|
], |
||||
|
noVUConnectionReuse: true, |
||||
|
}; |
||||
|
|
||||
|
const realmCount = 10; |
||||
|
const realm = pickRealm(realmCount); |
||||
|
const realmId = realm.id; |
||||
|
|
||||
|
randomSeed(__VU); |
||||
|
|
||||
|
const BASE_URL = `http://hp-microserver.itix.fr/auth/realms/${realmId}`; |
||||
|
const LOGIN_ENDPOINT = `${BASE_URL}/protocol/openid-connect/auth`; |
||||
|
const TOKEN_ENDPOINT = `${BASE_URL}/protocol/openid-connect/token`; |
||||
|
const UI_HEADERS = { |
||||
|
"Accept": "text/html,application/xhtml+xml,application/xml", |
||||
|
"Accept-Encoding": "gzip, deflate", |
||||
|
"Accept-Language": "en-US,en;q=0.5", |
||||
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0", |
||||
|
}; |
||||
|
|
||||
|
const LOGIN_PARAMS = { |
||||
|
"login": "true", |
||||
|
"response_type": "code", |
||||
|
}; |
||||
|
|
||||
|
function testKCLogin() { |
||||
|
var user = pickUser(realm); |
||||
|
var client = pickClient(realm); |
||||
|
|
||||
|
group('login', () => { |
||||
|
let login_params = Object.assign(LOGIN_PARAMS, { "client_id": client.clientId, "state": uuidv4(), "redirect_uri": client.redirectUris[0] }); |
||||
|
let query_string = buildQueryString(login_params); |
||||
|
let login_page = http.get(`${LOGIN_ENDPOINT}?${query_string}`, { "headers": UI_HEADERS, "tags": { name: "get-login-page" } }); |
||||
|
check(login_page, { |
||||
|
'login_page.status == 200': (http) => http.status === 200, |
||||
|
}); |
||||
|
|
||||
|
if (login_page == null || login_page.status !== 200) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
let authorization_response = login_page.submitForm({ |
||||
|
formSelector: '#kc-form-login', |
||||
|
fields: { username: user.username, password: user.credentials[0].value }, |
||||
|
params: { redirects: 0, "tags": { name: "authorization-request" } }, |
||||
|
}); |
||||
|
|
||||
|
check(authorization_response, { |
||||
|
'authorization_response.status == 302': (http) => http.status === 302, |
||||
|
}); |
||||
|
|
||||
|
let location = authorization_response.headers["Location"]; |
||||
|
let re = /[&?]code=([^&]+)(&|$)/; |
||||
|
let matches = [... location.matchAll(re) ]; |
||||
|
let code = matches[0][1]; |
||||
|
|
||||
|
let access_token_request = { |
||||
|
"grant_type": "authorization_code", |
||||
|
"code": code, |
||||
|
"redirect_uri": client.redirectUris[0], |
||||
|
"client_id": client.clientId, |
||||
|
"client_secret": client.secret |
||||
|
}; |
||||
|
let access_token_response = http.post(`${TOKEN_ENDPOINT}`, access_token_request, { "tags": { name: "access-token-request" } }); |
||||
|
|
||||
|
check(access_token_response, { |
||||
|
'access_token_response.status == 200': (http) => http.status === 200, |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
sleep(2); |
||||
|
} |
||||
|
|
||||
|
export default wrapWithErrorCounting(testKCLogin); |
||||
|
|
||||
Loading…
Reference in new issue