Compare commits
	
		
			39 Commits
		
	
	
		
			version/20
			...
			benchmarks
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cde4e395e9 | |||
| d19c692f81 | |||
| d5d2be5672 | |||
| 8597db59f5 | |||
| 74fb9492bc | |||
| defbafb55e | |||
| e2ed7391bc | |||
| 8dcd0dcaa9 | |||
| 18eee1b722 | |||
| d0f6c815c3 | |||
| b13eba3b0a | |||
| 77fe4e9fe2 | |||
| 71fe8b4fb3 | |||
| b14cb832b2 | |||
| 24b5296d88 | |||
| 41b7e50bc6 | |||
| 6b750d7c59 | |||
| d268c28934 | |||
| 688404b6a5 | |||
| cbd2425a5f | |||
| 877c264d59 | |||
| 2575b540fa | |||
| 0e0b76a62e | |||
| 6d625fd1d7 | |||
| bd0630e300 | |||
| ffb7d44024 | |||
| 7589b11f98 | |||
| ad21dfa2bc | |||
| 95692f5a7c | |||
| 1f4ed1defa | |||
| 334b183465 | |||
| 1f789dd4c5 | |||
| 057e5747c9 | |||
| 8717a3aaab | |||
| 527173236a | |||
| 3e6eb6f248 | |||
| 6babf0f1c4 | |||
| ca7cc30504 | |||
| a7cb808cad | 
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -209,3 +209,10 @@ source_docs/
 | 
			
		||||
 | 
			
		||||
### Golang ###
 | 
			
		||||
/vendor/
 | 
			
		||||
 | 
			
		||||
### Benchmark ###
 | 
			
		||||
tests/benchmark/k6
 | 
			
		||||
tests/benchmark/prometheus
 | 
			
		||||
tests/benchmark/**/*.json
 | 
			
		||||
tests/benchmark/**/*.ndjson
 | 
			
		||||
tests/benchmark/**/*.html
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Makefile
									
									
									
									
									
								
							@ -278,3 +278,20 @@ ci-bandit: ci--meta-debug
 | 
			
		||||
 | 
			
		||||
ci-pending-migrations: ci--meta-debug
 | 
			
		||||
	ak makemigrations --check
 | 
			
		||||
 | 
			
		||||
#########################
 | 
			
		||||
## Benchmark
 | 
			
		||||
#########################
 | 
			
		||||
 | 
			
		||||
benchmark-fixtures-create:
 | 
			
		||||
	tests/benchmark/fixtures.py create
 | 
			
		||||
 | 
			
		||||
benchmark-run:
 | 
			
		||||
	docker compose -f tests/benchmark/docker-compose.yml up -d
 | 
			
		||||
	sleep 5
 | 
			
		||||
	tests/benchmark/run.sh
 | 
			
		||||
 | 
			
		||||
benchmark-fixtures-delete:
 | 
			
		||||
	tests/benchmark/fixtures.py delete
 | 
			
		||||
 | 
			
		||||
benchmark: benchmark-fixtures-create benchmark-run benchmark-fixtures-delete
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ services:
 | 
			
		||||
  postgresql:
 | 
			
		||||
    container_name: postgres
 | 
			
		||||
    image: docker.io/library/postgres:16
 | 
			
		||||
    command: "-c max_connections=500"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - db-data:/var/lib/postgresql/data
 | 
			
		||||
    environment:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										0
									
								
								tests/benchmark/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/benchmark/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										30
									
								
								tests/benchmark/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tests/benchmark/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
---
 | 
			
		||||
services:
 | 
			
		||||
  prometheus:
 | 
			
		||||
    image: quay.io/prometheus/prometheus:latest
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    command:
 | 
			
		||||
      - --enable-feature=native-histograms
 | 
			
		||||
      - --web.enable-remote-write-receiver
 | 
			
		||||
      - --config.file=/etc/prometheus/prometheus.yml
 | 
			
		||||
      - --storage.tsdb.path=/prometheus
 | 
			
		||||
      - --web.console.libraries=/usr/share/prometheus/console_libraries
 | 
			
		||||
      - --web.console.templates=/usr/share/prometheus/consoles
 | 
			
		||||
    ports:
 | 
			
		||||
      - 127.0.0.1:9090:9090
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
 | 
			
		||||
      - ./prometheus:/prometheus
 | 
			
		||||
    user: root
 | 
			
		||||
 | 
			
		||||
  grafana:
 | 
			
		||||
    image: grafana/grafana:latest
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    environment:
 | 
			
		||||
      GF_AUTH_ANONYMOUS_ENABLED: "true"
 | 
			
		||||
      GF_AUTH_ANONYMOUS_ORG_ROLE: Admin
 | 
			
		||||
    ports:
 | 
			
		||||
      - 127.0.0.1:3000:3000
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
 | 
			
		||||
      - ./grafana/dashboards:/var/lib/grafana/dashboards:ro
 | 
			
		||||
							
								
								
									
										79
									
								
								tests/benchmark/event_list.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								tests/benchmark/event_list.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
import exec from "k6/execution";
 | 
			
		||||
import http from "k6/http";
 | 
			
		||||
import { check } from "k6";
 | 
			
		||||
 | 
			
		||||
const host = __ENV.BENCH_HOST ? __ENV.BENCH_HOST : "localhost";
 | 
			
		||||
const VUs = __ENV.VUS ? __ENV.VUS : 8;
 | 
			
		||||
 | 
			
		||||
export const options = {
 | 
			
		||||
    discardResponseBodies: true,
 | 
			
		||||
    scenarios: Object.fromEntries(
 | 
			
		||||
        [
 | 
			
		||||
            // Number of events, page size
 | 
			
		||||
            [1000, 100],
 | 
			
		||||
            [10000, 20],
 | 
			
		||||
            [10000, 100],
 | 
			
		||||
            [100000, 100],
 | 
			
		||||
            [1000000, 100],
 | 
			
		||||
        ].map((obj, i) => [
 | 
			
		||||
            `${obj[0]}_${obj[1]}`,
 | 
			
		||||
            {
 | 
			
		||||
                executor: "constant-vus",
 | 
			
		||||
                vus: VUs,
 | 
			
		||||
                duration: "150s",
 | 
			
		||||
                startTime: `${165 * i}s`,
 | 
			
		||||
                env: {
 | 
			
		||||
                    EVENT_COUNT: `${obj[0]}`,
 | 
			
		||||
                    PAGE_SIZE: `${obj[1]}`,
 | 
			
		||||
                },
 | 
			
		||||
                tags: {
 | 
			
		||||
                    testid: `event_list_${obj[0]}_${obj[1]}`,
 | 
			
		||||
                    event_count: `${obj[0]}`,
 | 
			
		||||
                    page_size: `${obj[1]}`,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        ]),
 | 
			
		||||
    ),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    const event_count = Number(__ENV.EVENT_COUNT);
 | 
			
		||||
    const domain = `event-list-${event_count}.${host}:9000`;
 | 
			
		||||
    const page_size = Number(__ENV.PAGE_SIZE);
 | 
			
		||||
    const pages = Math.round(event_count / page_size);
 | 
			
		||||
    const params = {
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: "Bearer akadmin",
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Accept: "*/*",
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (pages <= 10) {
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            let res = http.get(
 | 
			
		||||
                http.url`http://${domain}/api/v3/events/events/?page=${page}&page_size=${page_size}`,
 | 
			
		||||
                params,
 | 
			
		||||
            );
 | 
			
		||||
            check(res, {
 | 
			
		||||
                "status is 200": (res) => res.status === 200,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        let requests = [];
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            requests.push([
 | 
			
		||||
                "GET",
 | 
			
		||||
                http.url`http://${domain}/api/v3/events/events/?page=${page}&page_size=${page_size}`,
 | 
			
		||||
                null,
 | 
			
		||||
                params,
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
        const responses = http.batch(requests);
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            check(responses[page - 1], {
 | 
			
		||||
                "status is 200": (res) => res.status === 200,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										345
									
								
								tests/benchmark/fixtures.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										345
									
								
								tests/benchmark/fixtures.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,345 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
from collections.abc import Iterable
 | 
			
		||||
from multiprocessing import Process
 | 
			
		||||
from os import environ
 | 
			
		||||
from uuid import uuid4
 | 
			
		||||
 | 
			
		||||
import django
 | 
			
		||||
 | 
			
		||||
environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
 | 
			
		||||
environ.setdefault("AUTHENTIK_BOOTSTRAP_PASSWORD", "akadmin")
 | 
			
		||||
environ.setdefault("AUTHENTIK_BOOTSTRAP_TOKEN", "akadmin")
 | 
			
		||||
environ.setdefault("AUTHENTIK_BOOTSTRAP_EMAIL", "akadmin@authentik.test")
 | 
			
		||||
django.setup()
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
from authentik.core.models import Application, Group, User
 | 
			
		||||
from authentik.crypto.models import CertificateKeyPair
 | 
			
		||||
from authentik.events.models import Event, EventAction
 | 
			
		||||
from authentik.flows.models import Flow
 | 
			
		||||
from authentik.policies.expression.models import ExpressionPolicy
 | 
			
		||||
from authentik.policies.models import PolicyBinding
 | 
			
		||||
from authentik.providers.oauth2.models import OAuth2Provider
 | 
			
		||||
from authentik.stages.authenticator_static.models import StaticToken
 | 
			
		||||
from authentik.tenants.models import Domain, Tenant
 | 
			
		||||
 | 
			
		||||
settings.CELERY["task_always_eager"] = True
 | 
			
		||||
 | 
			
		||||
host = environ.get("BENCH_HOST", "localhost")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestSuite:
 | 
			
		||||
    TEST_NAME: str
 | 
			
		||||
    TEST_CASES: Iterable[Iterable[int | str | bool]]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_testcases(cls):
 | 
			
		||||
        return [cls(params) for params in cls.TEST_CASES]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, params: Iterable[int | str | bool]):
 | 
			
		||||
        self.params = params
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return (
 | 
			
		||||
            "-".join([self.TEST_NAME] + [str(param) for param in self.params])
 | 
			
		||||
            .replace("_", "-")
 | 
			
		||||
            .lower()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def schema_name(self):
 | 
			
		||||
        return f"t_{str(self).replace('-', '_')}"
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def domain_name(self):
 | 
			
		||||
        return f"{str(self)}.{host}"
 | 
			
		||||
 | 
			
		||||
    def create(self):
 | 
			
		||||
        created = False
 | 
			
		||||
        t = Tenant.objects.filter(schema_name=self.schema_name).first()
 | 
			
		||||
        if not t:
 | 
			
		||||
            created = True
 | 
			
		||||
            t = Tenant.objects.create(schema_name=self.schema_name, name=uuid4())
 | 
			
		||||
        Domain.objects.get_or_create(tenant=t, domain=self.domain_name)
 | 
			
		||||
        if created:
 | 
			
		||||
            with t:
 | 
			
		||||
                self.create_data(*self.params)
 | 
			
		||||
 | 
			
		||||
    def create_data(self):
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    def delete(self):
 | 
			
		||||
        Tenant.objects.filter(schema_name=self.schema_name).delete()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserList(TestSuite):
 | 
			
		||||
    TEST_NAME = "user-list"
 | 
			
		||||
    TEST_CASES = [
 | 
			
		||||
        (1000, 0, 0),
 | 
			
		||||
        (10000, 0, 0),
 | 
			
		||||
        (1000, 3, 0),
 | 
			
		||||
        (10000, 3, 0),
 | 
			
		||||
        (1000, 20, 0),
 | 
			
		||||
        (10000, 20, 0),
 | 
			
		||||
        (1000, 20, 3),
 | 
			
		||||
        (10000, 20, 3),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def create_data(self, user_count: int, groups_per_user: int, parents_per_group: int):
 | 
			
		||||
        Group.objects.bulk_create([Group(name=uuid4()) for _ in range(groups_per_user * 5)])
 | 
			
		||||
        for group in Group.objects.exclude(name="authentik Admins"):
 | 
			
		||||
            for _ in range(parents_per_group):
 | 
			
		||||
                new_group = Group.objects.create(name=uuid4())
 | 
			
		||||
                group.parent = new_group
 | 
			
		||||
                group.save()
 | 
			
		||||
                group = new_group
 | 
			
		||||
        User.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                User(
 | 
			
		||||
                    username=uuid4(),
 | 
			
		||||
                    name=uuid4(),
 | 
			
		||||
                )
 | 
			
		||||
                for _ in range(user_count)
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        if groups_per_user:
 | 
			
		||||
            for user in User.objects.exclude_anonymous().exclude(username="akadmin"):
 | 
			
		||||
                user.ak_groups.set(
 | 
			
		||||
                    Group.objects.exclude(name="authentik Admins").order_by("?")[:groups_per_user]
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GroupList(TestSuite):
 | 
			
		||||
    TEST_NAME = "group-list"
 | 
			
		||||
    TEST_CASES = [
 | 
			
		||||
        (1000, 0, False),
 | 
			
		||||
        (10000, 0, False),
 | 
			
		||||
        (1000, 1000, False),
 | 
			
		||||
        (1000, 10000, False),
 | 
			
		||||
        (1000, 0, True),
 | 
			
		||||
        (10000, 0, True),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def create_data(self, group_count, users_per_group, with_parent):
 | 
			
		||||
        User.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                User(
 | 
			
		||||
                    username=uuid4(),
 | 
			
		||||
                    name=uuid4(),
 | 
			
		||||
                )
 | 
			
		||||
                for _ in range(users_per_group * 5)
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        if with_parent:
 | 
			
		||||
            parents = Group.objects.bulk_create([Group(name=uuid4()) for _ in range(group_count)])
 | 
			
		||||
        groups = Group.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                Group(name=uuid4(), parent=(parents[i] if with_parent else None))
 | 
			
		||||
                for i in range(group_count)
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        if users_per_group:
 | 
			
		||||
            for group in groups:
 | 
			
		||||
                group.users.set(
 | 
			
		||||
                    User.objects.exclude_anonymous()
 | 
			
		||||
                    .exclude(username="akadmin")
 | 
			
		||||
                    .order_by("?")[:users_per_group]
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Login(TestSuite):
 | 
			
		||||
    TEST_NAME = "login"
 | 
			
		||||
    TEST_CASES = [
 | 
			
		||||
        ("no-mfa",),
 | 
			
		||||
        ("with-mfa",),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def create_data(self, mfa: str):
 | 
			
		||||
        user = User(username="test", name=uuid4())
 | 
			
		||||
        user.set_password("verySecurePassword")
 | 
			
		||||
        user.save()
 | 
			
		||||
 | 
			
		||||
        if mfa == "with-mfa":
 | 
			
		||||
            device = user.staticdevice_set.create()
 | 
			
		||||
            # Multiple token with same token for all the iterations in the test
 | 
			
		||||
            device.token_set.bulk_create(
 | 
			
		||||
                [StaticToken(device=device, token=f"staticToken") for _ in range(1_000_000)]
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProviderOauth2(TestSuite):
 | 
			
		||||
    TEST_NAME = "provider-oauth2"
 | 
			
		||||
    TEST_CASES = [
 | 
			
		||||
        (2, 50, 2),
 | 
			
		||||
        (0, 0, 0),
 | 
			
		||||
        (10, 0, 0),
 | 
			
		||||
        (100, 0, 0),
 | 
			
		||||
        (0, 10, 0),
 | 
			
		||||
        (0, 100, 0),
 | 
			
		||||
        (0, 0, 10),
 | 
			
		||||
        (0, 0, 100),
 | 
			
		||||
        (10, 10, 10),
 | 
			
		||||
        (100, 100, 100),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def create_data(
 | 
			
		||||
        self, user_policies_count: int, group_policies_count: int, expression_policies_count: int
 | 
			
		||||
    ):
 | 
			
		||||
        user = User(username="test", name=uuid4())
 | 
			
		||||
        user.set_password("verySecurePassword")
 | 
			
		||||
        user.save()
 | 
			
		||||
 | 
			
		||||
        provider = OAuth2Provider.objects.create(
 | 
			
		||||
            name="test",
 | 
			
		||||
            authorization_flow=Flow.objects.get(
 | 
			
		||||
                slug="default-provider-authorization-implicit-consent"
 | 
			
		||||
            ),
 | 
			
		||||
            signing_key=CertificateKeyPair.objects.get(name="authentik Self-signed Certificate"),
 | 
			
		||||
            redirect_uris="http://test.localhost",
 | 
			
		||||
            client_id="123456",
 | 
			
		||||
            client_secret="123456",
 | 
			
		||||
        )
 | 
			
		||||
        application = Application.objects.create(slug="test", name="test", provider=provider)
 | 
			
		||||
 | 
			
		||||
        User.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                User(
 | 
			
		||||
                    username=uuid4(),
 | 
			
		||||
                    name=uuid4(),
 | 
			
		||||
                )
 | 
			
		||||
                for _ in range(user_policies_count)
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        PolicyBinding.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                PolicyBinding(
 | 
			
		||||
                    user=user,
 | 
			
		||||
                    target=application,
 | 
			
		||||
                    order=random.randint(1, 1_000_000),
 | 
			
		||||
                )
 | 
			
		||||
                for user in User.objects.exclude(username="akadmin").exclude_anonymous()
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        Group.objects.bulk_create([Group(name=uuid4()) for _ in range(group_policies_count)])
 | 
			
		||||
        PolicyBinding.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                PolicyBinding(
 | 
			
		||||
                    group=group,
 | 
			
		||||
                    target=application,
 | 
			
		||||
                    order=random.randint(1, 1_000_000),
 | 
			
		||||
                )
 | 
			
		||||
                for group in Group.objects.exclude(name="authentik Admins")
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        user.ak_groups.set(Group.objects.exclude(name="authentik Admins").order_by("?")[:1])
 | 
			
		||||
 | 
			
		||||
        [
 | 
			
		||||
            ExpressionPolicy(
 | 
			
		||||
                name=f"test-{uuid4()}",
 | 
			
		||||
                expression="return True",
 | 
			
		||||
            ).save()
 | 
			
		||||
            for _ in range(expression_policies_count)
 | 
			
		||||
        ]
 | 
			
		||||
        PolicyBinding.objects.bulk_create(
 | 
			
		||||
            [
 | 
			
		||||
                PolicyBinding(
 | 
			
		||||
                    policy=policy,
 | 
			
		||||
                    target=application,
 | 
			
		||||
                    order=random.randint(1, 1_000_000),
 | 
			
		||||
                )
 | 
			
		||||
                for policy in ExpressionPolicy.objects.filter(name__startswith="test-")
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EventList(TestSuite):
 | 
			
		||||
    TEST_NAME = "event-list"
 | 
			
		||||
    TEST_CASES = [
 | 
			
		||||
        (1_000,),
 | 
			
		||||
        (10_000,),
 | 
			
		||||
        (100_000,),
 | 
			
		||||
        (1_000_000,),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def create_data(self, event_count: int):
 | 
			
		||||
        for _ in range(event_count // 1000):
 | 
			
		||||
            Event.objects.bulk_create(
 | 
			
		||||
                [
 | 
			
		||||
                    Event(
 | 
			
		||||
                        user={
 | 
			
		||||
                            "pk": str(uuid4()),
 | 
			
		||||
                            "name": str(uuid4()),
 | 
			
		||||
                            "username": str(uuid4()),
 | 
			
		||||
                            "email": f"{uuid4()}@example.org",
 | 
			
		||||
                        },
 | 
			
		||||
                        action="custom_benchmark",
 | 
			
		||||
                        app="tests_benchmarks",
 | 
			
		||||
                        context={
 | 
			
		||||
                            str(uuid4()): str(uuid4()),
 | 
			
		||||
                            str(uuid4()): str(uuid4()),
 | 
			
		||||
                            str(uuid4()): str(uuid4()),
 | 
			
		||||
                            str(uuid4()): str(uuid4()),
 | 
			
		||||
                            str(uuid4()): str(uuid4()),
 | 
			
		||||
                        },
 | 
			
		||||
                        client_ip="192.0.2.42",
 | 
			
		||||
                    )
 | 
			
		||||
                    for _ in range(1000)
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserGroupCreate(TestSuite):
 | 
			
		||||
    TEST_NAME = "user-group-create"
 | 
			
		||||
    TEST_CASES = [
 | 
			
		||||
        (),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def create_data(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(action: str, selected_suite: str | None = None):
 | 
			
		||||
    testsuites = TestSuite.__subclasses__()
 | 
			
		||||
    testcases = []
 | 
			
		||||
    for testsuite in testsuites:
 | 
			
		||||
        testcases += testsuite.get_testcases()
 | 
			
		||||
 | 
			
		||||
    match action:
 | 
			
		||||
        case "create":
 | 
			
		||||
            to_create = []
 | 
			
		||||
            for testcase in testcases:
 | 
			
		||||
                if selected_suite and testcase.TEST_NAME != selected_suite:
 | 
			
		||||
                    continue
 | 
			
		||||
                testcase.create()
 | 
			
		||||
            #     to_create.append(testcase)
 | 
			
		||||
            # processes = [Process(target=testcase.create) for testcase in to_create]
 | 
			
		||||
            # for p in processes:
 | 
			
		||||
            #     p.start()
 | 
			
		||||
            # for p in processes:
 | 
			
		||||
            #     p.join()
 | 
			
		||||
        case "list":
 | 
			
		||||
            print(*[testsuite.TEST_NAME for testsuite in testsuites], sep="\n")
 | 
			
		||||
        case "delete":
 | 
			
		||||
            for testcase in testcases:
 | 
			
		||||
                if selected_suite and testcase.TEST_NAME != selected_suite:
 | 
			
		||||
                    continue
 | 
			
		||||
                testcase.delete()
 | 
			
		||||
        case _:
 | 
			
		||||
            print("Unknown action. Should be create, list or delete")
 | 
			
		||||
            exit(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    if len(sys.argv) < 2:
 | 
			
		||||
        action = "create"
 | 
			
		||||
    else:
 | 
			
		||||
        action = sys.argv[1]
 | 
			
		||||
    if len(sys.argv) < 3:
 | 
			
		||||
        testsuite = None
 | 
			
		||||
    else:
 | 
			
		||||
        testsuite = sys.argv[2]
 | 
			
		||||
    main(action, testsuite)
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
---
 | 
			
		||||
apiVersion: 1
 | 
			
		||||
providers:
 | 
			
		||||
  - name: default
 | 
			
		||||
    folder: k6
 | 
			
		||||
    type: file
 | 
			
		||||
    options:
 | 
			
		||||
      path: /var/lib/grafana/dashboards
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
---
 | 
			
		||||
apiVersion: 1
 | 
			
		||||
datasources:
 | 
			
		||||
  - name: Prometheus
 | 
			
		||||
    type: prometheus
 | 
			
		||||
    access: proxy
 | 
			
		||||
    orgId: 1
 | 
			
		||||
    uid: prometheus
 | 
			
		||||
    url: http://prometheus:9090
 | 
			
		||||
    jsonData:
 | 
			
		||||
      timeInterval: 1s
 | 
			
		||||
							
								
								
									
										93
									
								
								tests/benchmark/group_list.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								tests/benchmark/group_list.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
			
		||||
import exec from "k6/execution";
 | 
			
		||||
import http from "k6/http";
 | 
			
		||||
import { check } from "k6";
 | 
			
		||||
 | 
			
		||||
const host = __ENV.BENCH_HOST ? __ENV.BENCH_HOST : "localhost";
 | 
			
		||||
const VUs = __ENV.VUS ? __ENV.VUS : 8;
 | 
			
		||||
 | 
			
		||||
export const options = {
 | 
			
		||||
    discardResponseBodies: true,
 | 
			
		||||
    scenarios: Object.fromEntries(
 | 
			
		||||
        [
 | 
			
		||||
            // Number of groups, number of users per group, with parent, page size, include users
 | 
			
		||||
            [1000, 0, false, 20, false],
 | 
			
		||||
            [10000, 0, false, 20, false],
 | 
			
		||||
            [1000, 0, false, 100, false],
 | 
			
		||||
            [10000, 0, false, 100, false],
 | 
			
		||||
            [1000, 1000, false, 100, false],
 | 
			
		||||
            [1000, 10000, false, 100, false],
 | 
			
		||||
            [1000, 1000, false, 100, true],
 | 
			
		||||
            [1000, 10000, false, 100, true],
 | 
			
		||||
            [1000, 0, true, 100, false],
 | 
			
		||||
            [10000, 0, true, 100, false],
 | 
			
		||||
        ].map((obj, i) => [
 | 
			
		||||
            `${obj[0]}_${obj[1]}_${obj[2] ? "with_parents" : "without_parents"}_${obj[3]}_${obj[4] ? "with_users" : "without_users"}`,
 | 
			
		||||
            {
 | 
			
		||||
                executor: "constant-vus",
 | 
			
		||||
                vus: VUs,
 | 
			
		||||
                duration: "150s",
 | 
			
		||||
                startTime: `${165 * i}s`,
 | 
			
		||||
                env: {
 | 
			
		||||
                    GROUP_COUNT: `${obj[0]}`,
 | 
			
		||||
                    USERS_PER_GROUP: `${obj[1]}`,
 | 
			
		||||
                    WITH_PARENTS: `${obj[2]}`,
 | 
			
		||||
                    PAGE_SIZE: `${obj[3]}`,
 | 
			
		||||
                    WITH_USERS: `${obj[4] ? "true" : "false"}`,
 | 
			
		||||
                },
 | 
			
		||||
                tags: {
 | 
			
		||||
                    testid: `group_list_${obj[0]}_${obj[1]}_${obj[2] ? "with_parents" : "without_parents"}_${obj[3]}_${obj[4] ? "with_users" : "without_users"}`,
 | 
			
		||||
                    group_count: `${obj[0]}`,
 | 
			
		||||
                    users_per_group: `${obj[1]}`,
 | 
			
		||||
                    with_parents: `${obj[2]}`,
 | 
			
		||||
                    page_size: `${obj[3]}`,
 | 
			
		||||
                    with_users: `${obj[4] ? "true" : "false"}`,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        ]),
 | 
			
		||||
    ),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    const group_count = Number(__ENV.GROUP_COUNT);
 | 
			
		||||
    const users_per_group = Number(__ENV.USERS_PER_GROUP);
 | 
			
		||||
    const with_parents = __ENV.WITH_PARENTS;
 | 
			
		||||
    const with_users = __ENV.WITH_USERS;
 | 
			
		||||
    const domain = `group-list-${group_count}-${users_per_group}-${with_parents}.${host}:9000`;
 | 
			
		||||
    const page_size = Number(__ENV.PAGE_SIZE);
 | 
			
		||||
    const pages = Math.round(group_count / page_size);
 | 
			
		||||
    const params = {
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: "Bearer akadmin",
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Accept: "*/*",
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (pages <= 10) {
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            let res = http.get(
 | 
			
		||||
                http.url`http://${domain}/api/v3/core/groups/?page=${page}&page_size=${page_size}&include_users=${with_users}`,
 | 
			
		||||
                params,
 | 
			
		||||
            );
 | 
			
		||||
            check(res, {
 | 
			
		||||
                "status is 200": (res) => res.status === 200,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        let requests = [];
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            requests.push([
 | 
			
		||||
                "GET",
 | 
			
		||||
                http.url`http://${domain}/api/v3/core/groups/?page=${page}&page_size=${page_size}&include_users=${with_users}`,
 | 
			
		||||
                null,
 | 
			
		||||
                params,
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
        const responses = http.batch(requests);
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            check(responses[page - 1], {
 | 
			
		||||
                "status is 200": (res) => res.status === 200,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								tests/benchmark/login.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								tests/benchmark/login.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
import http from "k6/http";
 | 
			
		||||
import { check, fail } from "k6";
 | 
			
		||||
 | 
			
		||||
const host = __ENV.BENCH_HOST ? __ENV.BENCH_HOST : "localhost";
 | 
			
		||||
const VUs = __ENV.VUS ? __ENV.VUS : 8;
 | 
			
		||||
 | 
			
		||||
export const options = {
 | 
			
		||||
    scenarios: Object.fromEntries(
 | 
			
		||||
        ["no-mfa", "with-mfa"].map((obj, i) => [
 | 
			
		||||
            obj,
 | 
			
		||||
            {
 | 
			
		||||
                executor: "constant-vus",
 | 
			
		||||
                vus: VUs,
 | 
			
		||||
                duration: "150s",
 | 
			
		||||
                startTime: `${165 * i}s`,
 | 
			
		||||
                env: {
 | 
			
		||||
                    DOMAIN: `login-${obj}`,
 | 
			
		||||
                },
 | 
			
		||||
                tags: {
 | 
			
		||||
                    testid: `login-${obj}`,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        ]),
 | 
			
		||||
    ),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    const domain = __ENV.DOMAIN;
 | 
			
		||||
    const url = http.url`http://${domain}.${host}:9000/api/v3/flows/executor/default-authentication-flow/`;
 | 
			
		||||
    const cookieJar = new http.CookieJar();
 | 
			
		||||
    const params = {
 | 
			
		||||
        jar: cookieJar,
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Accept: "*/*",
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
    let res = http.get(url, params);
 | 
			
		||||
    let i = 0;
 | 
			
		||||
    while (true) {
 | 
			
		||||
        if (i > 10) {
 | 
			
		||||
            fail("Test made more than 10 queries.");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        check(res, {
 | 
			
		||||
            "status is 200": (res) => res.status === 200,
 | 
			
		||||
        });
 | 
			
		||||
        if (res.status !== 200) {
 | 
			
		||||
            fail("Endpoint did not return 200.");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const component = res.json()["component"];
 | 
			
		||||
        let payload = {};
 | 
			
		||||
        if (component === "ak-stage-identification") {
 | 
			
		||||
            payload = {
 | 
			
		||||
                uid_field: "test",
 | 
			
		||||
            };
 | 
			
		||||
        } else if (component === "ak-stage-password") {
 | 
			
		||||
            payload = {
 | 
			
		||||
                password: "verySecurePassword",
 | 
			
		||||
            };
 | 
			
		||||
        } else if (component === "ak-stage-authenticator-validate") {
 | 
			
		||||
            payload = {
 | 
			
		||||
                code: "staticToken",
 | 
			
		||||
            };
 | 
			
		||||
        } else if (component === "xak-flow-redirect") {
 | 
			
		||||
            break;
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log(`Unknown component type: ${component}`);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        payload["component"] = component;
 | 
			
		||||
        res = http.post(url, JSON.stringify(payload), params);
 | 
			
		||||
        i++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								tests/benchmark/prometheus.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/benchmark/prometheus.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
---
 | 
			
		||||
global:
 | 
			
		||||
  scrape_interval: 15s
 | 
			
		||||
  evaluation_interval: 15s
 | 
			
		||||
  external_labels:
 | 
			
		||||
    cluster: benchmarks
 | 
			
		||||
    prometheus: benchmarks
 | 
			
		||||
    prometheus_replica: "0"
 | 
			
		||||
 | 
			
		||||
scrape_configs:
 | 
			
		||||
  - job_name: prometheus
 | 
			
		||||
    static_configs:
 | 
			
		||||
      - targets: ["localhost:9090"]
 | 
			
		||||
							
								
								
									
										179
									
								
								tests/benchmark/provider_oauth2.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								tests/benchmark/provider_oauth2.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,179 @@
 | 
			
		||||
import crypto from "k6/crypto";
 | 
			
		||||
import exec from "k6/execution";
 | 
			
		||||
import http from "k6/http";
 | 
			
		||||
import { check, fail } from "k6";
 | 
			
		||||
 | 
			
		||||
const host = __ENV.BENCH_HOST ? __ENV.BENCH_HOST : "localhost";
 | 
			
		||||
const VUs = __ENV.VUS ? __ENV.VUS : 8;
 | 
			
		||||
 | 
			
		||||
const testcases = [
 | 
			
		||||
    [2, 50, 2],
 | 
			
		||||
    [0, 0, 0],
 | 
			
		||||
    [10, 0, 0],
 | 
			
		||||
    [100, 0, 0],
 | 
			
		||||
    [0, 10, 0],
 | 
			
		||||
    [0, 100, 0],
 | 
			
		||||
    [0, 0, 10],
 | 
			
		||||
    [0, 0, 100],
 | 
			
		||||
    [10, 10, 10],
 | 
			
		||||
    [100, 100, 100],
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const options = {
 | 
			
		||||
    setupTimeout: "10m",
 | 
			
		||||
    scenarios: Object.fromEntries(
 | 
			
		||||
        testcases.map((obj, i) => [
 | 
			
		||||
            `${obj[0]}_${obj[1]}_${obj[2]}`,
 | 
			
		||||
            {
 | 
			
		||||
                executor: "constant-vus",
 | 
			
		||||
                vus: VUs,
 | 
			
		||||
                duration: "150s",
 | 
			
		||||
                startTime: `${165 * i}s`,
 | 
			
		||||
                env: {
 | 
			
		||||
                    USER_POLICIES_COUNT: `${obj[0]}`,
 | 
			
		||||
                    GROUP_POLICIES_COUNT: `${obj[1]}`,
 | 
			
		||||
                    EXPRESSION_POLICIES_COUNT: `${obj[2]}`,
 | 
			
		||||
                },
 | 
			
		||||
                tags: {
 | 
			
		||||
                    testid: `provider-oauth2-${obj[0]}_${obj[1]}_${obj[2]}`,
 | 
			
		||||
                    user_policies_count: `${obj[0]}`,
 | 
			
		||||
                    group_policies_count: `${obj[1]}`,
 | 
			
		||||
                    expression_policies_count: `${obj[2]}`,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        ]),
 | 
			
		||||
    ),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function setup() {
 | 
			
		||||
    let cookies = {};
 | 
			
		||||
    for (let vu = 0; vu < VUs; vu++) {
 | 
			
		||||
        cookies[vu] = {};
 | 
			
		||||
        for (const testcase of testcases) {
 | 
			
		||||
            const user_policies_count = testcase[0];
 | 
			
		||||
            const group_policies_count = testcase[1];
 | 
			
		||||
            const expression_policies_count = testcase[2];
 | 
			
		||||
            const domain = `provider-oauth2-${user_policies_count}-${group_policies_count}-${expression_policies_count}.${host}:9000`;
 | 
			
		||||
            const url = http.url`http://${domain}/api/v3/flows/executor/default-authentication-flow/`;
 | 
			
		||||
            const params = {
 | 
			
		||||
                headers: {
 | 
			
		||||
                    "Content-Type": "application/json",
 | 
			
		||||
                    Accept: "*/*",
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            http.cookieJar().clear(`http://${domain}`);
 | 
			
		||||
            let res = http.get(url, params);
 | 
			
		||||
            let i = 0;
 | 
			
		||||
            while (true) {
 | 
			
		||||
                if (i > 10) {
 | 
			
		||||
                    fail("Test made more than 10 queries.");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                check(res, {
 | 
			
		||||
                    "status is 200": (res) => res.status === 200,
 | 
			
		||||
                });
 | 
			
		||||
                if (res.status !== 200) {
 | 
			
		||||
                    fail("Endpoint did not return 200.");
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const component = res.json()["component"];
 | 
			
		||||
                let payload = {};
 | 
			
		||||
                if (component === "ak-stage-identification") {
 | 
			
		||||
                    payload = {
 | 
			
		||||
                        uid_field: "test",
 | 
			
		||||
                    };
 | 
			
		||||
                } else if (component === "ak-stage-password") {
 | 
			
		||||
                    payload = {
 | 
			
		||||
                        password: "verySecurePassword",
 | 
			
		||||
                    };
 | 
			
		||||
                } else if (component === "xak-flow-redirect") {
 | 
			
		||||
                    break;
 | 
			
		||||
                } else {
 | 
			
		||||
                    fail(`Unknown component type: ${component}`);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                payload["component"] = component;
 | 
			
		||||
                res = http.post(url, JSON.stringify(payload), params);
 | 
			
		||||
                i++;
 | 
			
		||||
            }
 | 
			
		||||
            cookies[vu][domain] = http
 | 
			
		||||
                .cookieJar()
 | 
			
		||||
                .cookiesForURL(`http://${domain}`);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return { cookies };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function (data) {
 | 
			
		||||
    // Restore cookies
 | 
			
		||||
    let jar = http.cookieJar();
 | 
			
		||||
    const vu = exec.vu.idInTest % VUs;
 | 
			
		||||
    Object.keys(data.cookies[vu]).forEach((domain) => {
 | 
			
		||||
        Object.keys(data.cookies[vu][domain]).forEach((key) => {
 | 
			
		||||
            jar.set(`http://${domain}`, key, data.cookies[vu][domain][key][0]);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const user_policies_count = Number(__ENV.USER_POLICIES_COUNT);
 | 
			
		||||
    const group_policies_count = Number(__ENV.GROUP_POLICIES_COUNT);
 | 
			
		||||
    const expression_policies_count = Number(__ENV.EXPRESSION_POLICIES_COUNT);
 | 
			
		||||
    const domain = `provider-oauth2-${user_policies_count}-${group_policies_count}-${expression_policies_count}.${host}:9000`;
 | 
			
		||||
    const params = {
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Accept: "*/*",
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const random = (length = 32) => {
 | 
			
		||||
        let chars =
 | 
			
		||||
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 | 
			
		||||
        let str = "";
 | 
			
		||||
        for (let i = 0; i < length; i++) {
 | 
			
		||||
            str += chars.charAt(Math.floor(Math.random() * chars.length));
 | 
			
		||||
        }
 | 
			
		||||
        return str;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const state = random(32);
 | 
			
		||||
    const nonce = random(32);
 | 
			
		||||
    const code_verifier = random(64);
 | 
			
		||||
    const code_challenge = crypto.sha256(code_verifier, "base64");
 | 
			
		||||
    const urlParams = {
 | 
			
		||||
        response_type: "code",
 | 
			
		||||
        scope: "openid profile email",
 | 
			
		||||
        client_id: "123456",
 | 
			
		||||
        redirect_uri: "http://test.localhost",
 | 
			
		||||
        state: state,
 | 
			
		||||
        nonce: nonce,
 | 
			
		||||
        code_challenge: code_challenge,
 | 
			
		||||
        code_challenge_method: "S256",
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let url = http.url`http://${domain}/application/o/authorize/?${Object.entries(
 | 
			
		||||
        urlParams,
 | 
			
		||||
    )
 | 
			
		||||
        .map((kv) => kv.map(encodeURIComponent).join("="))
 | 
			
		||||
        .join("&")}`;
 | 
			
		||||
    let res = http.get(url, params);
 | 
			
		||||
    check(res, {
 | 
			
		||||
        "status is 200": (res) => res.status === 200,
 | 
			
		||||
    });
 | 
			
		||||
    if (res.status !== 200) {
 | 
			
		||||
        fail("Endpoint did not return 200.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    url = http.url`http://${domain}/api/v3/flows/executor/default-provider-authorization-implicit-consent/`;
 | 
			
		||||
    res = http.get(url, params);
 | 
			
		||||
    check(res, {
 | 
			
		||||
        "status is 200": (res) => res.status === 200,
 | 
			
		||||
        "last redirect is present": (res) => res.json()["type"] === "redirect",
 | 
			
		||||
    });
 | 
			
		||||
    if (res.status !== 200) {
 | 
			
		||||
        fail("Endpoint did not return 200.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								tests/benchmark/run.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								tests/benchmark/run.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -euo pipefail
 | 
			
		||||
 | 
			
		||||
BASE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
 | 
			
		||||
 | 
			
		||||
function _k6 {
 | 
			
		||||
    local filename="${1}"
 | 
			
		||||
 | 
			
		||||
    K6_PROMETHEUS_RW_SERVER_URL=${PROMETHEUS_REMOTE_WRITE_ENDPOINT:-http://localhost:9090/api/v1/write} \
 | 
			
		||||
    K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true \
 | 
			
		||||
    K6_PROMETHEUS_RW_PUSH_INTERVAL=1s \
 | 
			
		||||
    k6 run \
 | 
			
		||||
        --out experimental-prometheus-rw \
 | 
			
		||||
        --out "json=${filename%.*}.json" \
 | 
			
		||||
        "${@}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
filename=""
 | 
			
		||||
if [ "${#}" -ge 1 ]; then
 | 
			
		||||
    filename="${1:-}"
 | 
			
		||||
    shift
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if [ -f "${filename}" ]; then
 | 
			
		||||
    _k6 "${filename}" "${@}"
 | 
			
		||||
else
 | 
			
		||||
    find "${BASE_DIR}" -name '*.js' | while read -r f; do
 | 
			
		||||
        _k6 "${f}" "${@}"
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										68
									
								
								tests/benchmark/user_group_create.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								tests/benchmark/user_group_create.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
			
		||||
import exec from "k6/execution";
 | 
			
		||||
import http from "k6/http";
 | 
			
		||||
import { check } from "k6";
 | 
			
		||||
 | 
			
		||||
const host = __ENV.BENCH_HOST ? __ENV.BENCH_HOST : "localhost";
 | 
			
		||||
const VUs = __ENV.VUS ? __ENV.VUS : 8;
 | 
			
		||||
 | 
			
		||||
export const options = {
 | 
			
		||||
    vus: VUs,
 | 
			
		||||
    duration: "150s",
 | 
			
		||||
    tags: {
 | 
			
		||||
        testid: `user-group-create`,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    const domain = `user-group-create.${host}:9000`;
 | 
			
		||||
    const params = {
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: "Bearer akadmin",
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Accept: "*/*",
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
    const random = (length = 32) => {
 | 
			
		||||
        let chars =
 | 
			
		||||
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 | 
			
		||||
        let str = "";
 | 
			
		||||
        for (let i = 0; i < length; i++) {
 | 
			
		||||
            str += chars.charAt(Math.floor(Math.random() * chars.length));
 | 
			
		||||
        }
 | 
			
		||||
        return str;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let user_res = http.post(
 | 
			
		||||
        http.url`http://${domain}/api/v3/core/users/`,
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            username: random(16),
 | 
			
		||||
            name: random(16),
 | 
			
		||||
        }),
 | 
			
		||||
        params,
 | 
			
		||||
    );
 | 
			
		||||
    check(user_res, {
 | 
			
		||||
        "user status is 201": (res) => res.status === 201,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let group_res = http.post(
 | 
			
		||||
        http.url`http://${domain}/api/v3/core/groups/`,
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            name: random(16),
 | 
			
		||||
        }),
 | 
			
		||||
        params,
 | 
			
		||||
    );
 | 
			
		||||
    check(group_res, {
 | 
			
		||||
        "group status is 201": (res) => res.status === 201,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let user_group_res = http.post(
 | 
			
		||||
        http.url`http://${domain}/api/v3/core/groups/${group_res.json()["pk"]}/add_user/`,
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            pk: user_res.json()["pk"],
 | 
			
		||||
        }),
 | 
			
		||||
        params,
 | 
			
		||||
    );
 | 
			
		||||
    check(user_group_res, {
 | 
			
		||||
        "user group status is 204": (res) => res.status === 204,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								tests/benchmark/user_list.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								tests/benchmark/user_list.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
			
		||||
import exec from "k6/execution";
 | 
			
		||||
import http from "k6/http";
 | 
			
		||||
import { check } from "k6";
 | 
			
		||||
 | 
			
		||||
const host = __ENV.BENCH_HOST ? __ENV.BENCH_HOST : "localhost";
 | 
			
		||||
const VUs = __ENV.VUS ? __ENV.VUS : 8;
 | 
			
		||||
 | 
			
		||||
export const options = {
 | 
			
		||||
    discardResponseBodies: true,
 | 
			
		||||
    scenarios: Object.fromEntries(
 | 
			
		||||
        [
 | 
			
		||||
            // Number of users, number of groups per user, number of parents per group, page size, with groups
 | 
			
		||||
            [1000, 0, 0, 20, true],
 | 
			
		||||
            [10000, 0, 0, 20, true],
 | 
			
		||||
            [1000, 0, 0, 20, false],
 | 
			
		||||
            [10000, 0, 0, 20, false],
 | 
			
		||||
            [1000, 0, 0, 100, true],
 | 
			
		||||
            [10000, 0, 0, 100, true],
 | 
			
		||||
            [1000, 3, 0, 20, true],
 | 
			
		||||
            [10000, 3, 0, 20, true],
 | 
			
		||||
            [1000, 20, 0, 20, true],
 | 
			
		||||
            [10000, 20, 0, 20, true],
 | 
			
		||||
            [1000, 20, 3, 20, true],
 | 
			
		||||
            [10000, 20, 3, 20, true],
 | 
			
		||||
            [1000, 20, 0, 20, false],
 | 
			
		||||
            [10000, 20, 0, 20, false],
 | 
			
		||||
            [1000, 20, 3, 20, false],
 | 
			
		||||
            [10000, 20, 3, 20, false],
 | 
			
		||||
        ].map((obj, i) => [
 | 
			
		||||
            `${obj[0]}_${obj[1]}_${obj[2]}_${obj[3]}_${obj[4] ? "with_groups" : "without_groups"}`,
 | 
			
		||||
            {
 | 
			
		||||
                executor: "constant-vus",
 | 
			
		||||
                vus: VUs,
 | 
			
		||||
                duration: "150s",
 | 
			
		||||
                startTime: `${165 * i}s`,
 | 
			
		||||
                env: {
 | 
			
		||||
                    USER_COUNT: `${obj[0]}`,
 | 
			
		||||
                    GROUPS_PER_USER: `${obj[1]}`,
 | 
			
		||||
                    PARENTS_PER_GROUP: `${obj[2]}`,
 | 
			
		||||
                    PAGE_SIZE: `${obj[3]}`,
 | 
			
		||||
                    WITH_GROUPS: `${obj[4] ? "true" : "false"}`,
 | 
			
		||||
                },
 | 
			
		||||
                tags: {
 | 
			
		||||
                    testid: `user_list_${obj[0]}_${obj[1]}_${obj[2]}_${obj[3]}_${obj[4] ? "with_groups" : "without_groups"}`,
 | 
			
		||||
                    user_count: `${obj[0]}`,
 | 
			
		||||
                    groups_per_user: `${obj[1]}`,
 | 
			
		||||
                    parents_per_group: `${obj[2]}`,
 | 
			
		||||
                    page_size: `${obj[3]}`,
 | 
			
		||||
                    with_groups: `${obj[4] ? "true" : "false"}`,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        ]),
 | 
			
		||||
    ),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function () {
 | 
			
		||||
    const user_count = Number(__ENV.USER_COUNT);
 | 
			
		||||
    const groups_per_user = Number(__ENV.GROUPS_PER_USER);
 | 
			
		||||
    const parents_per_group = Number(__ENV.PARENTS_PER_GROUP);
 | 
			
		||||
    const with_groups = __ENV.WITH_GROUPS;
 | 
			
		||||
    const domain = `user-list-${user_count}-${groups_per_user}-${parents_per_group}.${host}:9000`;
 | 
			
		||||
    const page_size = Number(__ENV.PAGE_SIZE);
 | 
			
		||||
    const pages = Math.round(user_count / page_size);
 | 
			
		||||
    const params = {
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: "Bearer akadmin",
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Accept: "*/*",
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (pages <= 10) {
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            let res = http.get(
 | 
			
		||||
                http.url`http://${domain}/api/v3/core/users/?page=${page}&page_size=${page_size}&include_groups=${with_groups}`,
 | 
			
		||||
                params,
 | 
			
		||||
            );
 | 
			
		||||
            check(res, {
 | 
			
		||||
                "status is 100": (res) => res.status === 200,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        let requests = [];
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            requests.push([
 | 
			
		||||
                "GET",
 | 
			
		||||
                http.url`http://${domain}/api/v3/core/users/?page=${page}&page_size=${page_size}&include_groups=${with_groups}`,
 | 
			
		||||
                null,
 | 
			
		||||
                params,
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
        const responses = http.batch(requests);
 | 
			
		||||
        for (let page = 1; page <= pages; page++) {
 | 
			
		||||
            check(responses[page - 1], {
 | 
			
		||||
                "status is 200": (res) => res.status === 200,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user