From 24f045b3abd19f9f98bec92a5a72c16008c2374b Mon Sep 17 00:00:00 2001 From: Nicolas MASSE Date: Fri, 22 Jan 2021 19:27:50 +0100 Subject: [PATCH] initial commit --- README.md | 57 ++ data/.gitignore | 1 + grafana/dashboard.json | 1391 ++++++++++++++++++++++++++++++++++++++++ lib/keycloak.js | 53 ++ login.js | 85 +++ 5 files changed, 1587 insertions(+) create mode 100644 README.md create mode 100644 data/.gitignore create mode 100644 grafana/dashboard.json create mode 100644 lib/keycloak.js create mode 100644 login.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3e604a --- /dev/null +++ b/README.md @@ -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 +``` + diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/data/.gitignore @@ -0,0 +1 @@ +* diff --git a/grafana/dashboard.json b/grafana/dashboard.json new file mode 100644 index 0000000..a90567c --- /dev/null +++ b/grafana/dashboard.json @@ -0,0 +1,1391 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 4, + "iteration": 1611339409476, + "links": [], + "panels": [ + { + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 10, + "title": "Global metrics", + "type": "row" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "targets": [ + { + "expr": "rate(k6_iterations[$__rate_interval])", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Iterations per second", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "VUs" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "targets": [ + { + "expr": "k6_vus", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Virtual Users", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "decimals": 0, + "mappings": [], + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 0.01 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "targets": [ + { + "expr": "sum(rate(k6_check{outcome=\"fail\"}[$__rate_interval])) / sum(rate(k6_check[$__rate_interval]))", + "interval": "", + "legendFormat": "", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "% Failed Checks", + "transformations": [], + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 7, + "x": 12, + "y": 1 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "targets": [ + { + "expr": "sum(rate(k6_data_received[$__rate_interval]))", + "interval": "", + "legendFormat": "Received", + "queryType": "randomWalk", + "refId": "A" + }, + { + "expr": "sum(rate(k6_data_sent[$__rate_interval]))", + "interval": "", + "legendFormat": "Sent", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Throughput", + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 19, + "y": 1 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "targets": [ + { + "expr": "sum(k6_script_errors)", + "interval": "", + "legendFormat": "Script errors", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Errors (seen so far)", + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 7 + }, + "id": 14, + "panels": [], + "repeat": "K6RequestName", + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "access-token-request", + "value": "access-token-request" + } + }, + "title": "K6 HTTP Request: ${K6RequestName}", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "fill": 8, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": true, + "current": false, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": false + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "access-token-request", + "value": "access-token-request" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status) (rate(k6_http_reqs{name=\"${K6RequestName}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "HTTP {{status}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Throughput", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#F2495C", + "colorScale": "linear", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "mode": "spectrum" + }, + "dataFormat": "tsbuckets", + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 6, + "y": 8 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 20, + "legend": { + "show": false + }, + "pluginVersion": "7.3.6", + "reverseYBuckets": false, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "access-token-request", + "value": "access-token-request" + } + }, + "targets": [ + { + "expr": "sum(rate(k6_http_${GoHttpMetricName}_bucket{name=\"${K6RequestName}\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Latency (${GoHttpMetricName})", + "tooltip": { + "show": true, + "showHistogram": false + }, + "tooltipDecimals": null, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "short", + "logBase": 10, + "max": "10", + "min": "0", + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 4, + "x": 18, + "y": 8 + }, + "id": 25, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "access-token-request", + "value": "access-token-request" + } + }, + "targets": [ + { + "expr": "1000*sum (rate(k6_http_${GoHttpMetricName}_sum{name=\"${K6RequestName}\"}[$__rate_interval]))/sum (rate(k6_http_${GoHttpMetricName}_count{name=\"${K6RequestName}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "{{status}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Mean latency (${GoHttpMetricName})", + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 26, + "panels": [], + "repeatIteration": 1611339409476, + "repeatPanelId": 14, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "get-login-page", + "value": "get-login-page" + } + }, + "title": "K6 HTTP Request: ${K6RequestName}", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "fill": 4, + "fillGradient": 8, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 17 + }, + "hiddenSeries": false, + "id": 27, + "legend": { + "avg": true, + "current": false, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": false + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "repeatIteration": 1611339409476, + "repeatPanelId": 16, + "repeatedByRow": true, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "get-login-page", + "value": "get-login-page" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status) (rate(k6_http_reqs{name=\"${K6RequestName}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "HTTP {{status}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Throughput", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#F2495C", + "colorScale": "linear", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "mode": "spectrum" + }, + "dataFormat": "tsbuckets", + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 6, + "y": 17 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 28, + "legend": { + "show": false + }, + "pluginVersion": "7.3.6", + "repeatIteration": 1611339409476, + "repeatPanelId": 20, + "repeatedByRow": true, + "reverseYBuckets": false, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "get-login-page", + "value": "get-login-page" + } + }, + "targets": [ + { + "expr": "sum(rate(k6_http_${GoHttpMetricName}_bucket{name=\"${K6RequestName}\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Latency (${GoHttpMetricName})", + "tooltip": { + "show": true, + "showHistogram": false + }, + "tooltipDecimals": null, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "short", + "logBase": 10, + "max": "10", + "min": "0", + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 4, + "x": 18, + "y": 17 + }, + "id": 29, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "repeatIteration": 1611339409476, + "repeatPanelId": 25, + "repeatedByRow": true, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "get-login-page", + "value": "get-login-page" + } + }, + "targets": [ + { + "expr": "1000*sum (rate(k6_http_${GoHttpMetricName}_sum{name=\"${K6RequestName}\"}[$__rate_interval]))/sum (rate(k6_http_${GoHttpMetricName}_count{name=\"${K6RequestName}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "{{status}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Mean latency (${GoHttpMetricName})", + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 30, + "panels": [], + "repeatIteration": 1611339409476, + "repeatPanelId": 14, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "authorization-request", + "value": "authorization-request" + } + }, + "title": "K6 HTTP Request: ${K6RequestName}", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "fill": 4, + "fillGradient": 8, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 26 + }, + "hiddenSeries": false, + "id": 31, + "legend": { + "avg": true, + "current": false, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "alertThreshold": false + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "repeatIteration": 1611339409476, + "repeatPanelId": 16, + "repeatedByRow": true, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "authorization-request", + "value": "authorization-request" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status) (rate(k6_http_reqs{name=\"${K6RequestName}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "HTTP {{status}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Throughput", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#F2495C", + "colorScale": "linear", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "mode": "spectrum" + }, + "dataFormat": "tsbuckets", + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 6, + "y": 26 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 32, + "legend": { + "show": false + }, + "pluginVersion": "7.3.6", + "repeatIteration": 1611339409476, + "repeatPanelId": 20, + "repeatedByRow": true, + "reverseYBuckets": false, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "authorization-request", + "value": "authorization-request" + } + }, + "targets": [ + { + "expr": "sum(rate(k6_http_${GoHttpMetricName}_bucket{name=\"${K6RequestName}\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Latency (${GoHttpMetricName})", + "tooltip": { + "show": true, + "showHistogram": false + }, + "tooltipDecimals": null, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "short", + "logBase": 10, + "max": "10", + "min": "0", + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 4, + "x": 18, + "y": 26 + }, + "id": 33, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.3.6", + "repeatIteration": 1611339409476, + "repeatPanelId": 25, + "repeatedByRow": true, + "scopedVars": { + "K6RequestName": { + "selected": true, + "text": "authorization-request", + "value": "authorization-request" + } + }, + "targets": [ + { + "expr": "1000*sum (rate(k6_http_${GoHttpMetricName}_sum{name=\"${K6RequestName}\"}[$__rate_interval]))/sum (rate(k6_http_${GoHttpMetricName}_count{name=\"${K6RequestName}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "{{status}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Mean latency (${GoHttpMetricName})", + "type": "stat" + } + ], + "refresh": "1s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "selected": true, + "tags": [], + "text": [ + "access-token-request", + "authorization-request", + "get-login-page" + ], + "value": [ + "access-token-request", + "authorization-request", + "get-login-page" + ] + }, + "datasource": "Prometheus", + "definition": "label_values(k6_http_req_duration_count, name)", + "error": null, + "hide": 0, + "includeAll": false, + "label": "K6 Request Name", + "multi": true, + "name": "K6RequestName", + "options": [], + "query": "label_values(k6_http_req_duration_count, name)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "req_duration", + "value": "req_duration" + }, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Go HTTP Metric Name", + "multi": false, + "name": "GoHttpMetricName", + "options": [ + { + "selected": false, + "text": "req_blocked", + "value": "req_blocked" + }, + { + "selected": false, + "text": "req_connecting", + "value": "req_connecting" + }, + { + "selected": true, + "text": "req_duration", + "value": "req_duration" + }, + { + "selected": false, + "text": "req_receiving", + "value": "req_receiving" + }, + { + "selected": false, + "text": "req_sending", + "value": "req_sending" + }, + { + "selected": false, + "text": "req_tls_handshaking", + "value": "req_tls_handshaking" + }, + { + "selected": false, + "text": "req_waiting", + "value": "req_waiting" + } + ], + "query": "req_blocked,req_connecting,req_duration,req_receiving,req_sending,req_tls_handshaking,req_waiting", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "1s", + "5s", + "10s", + "30s" + ] + }, + "timezone": "", + "title": "K6-StatsD-Prometheus", + "uid": "zDpLnqaMz", + "version": 27 +} \ No newline at end of file diff --git a/lib/keycloak.js b/lib/keycloak.js new file mode 100644 index 0000000..ef6c772 --- /dev/null +++ b/lib/keycloak.js @@ -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("&"); +} diff --git a/login.js b/login.js new file mode 100644 index 0000000..63eeb6d --- /dev/null +++ b/login.js @@ -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); +