Compare commits
	
		
			39 Commits
		
	
	
		
			eap-but-ac
			...
			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 ### | ### Golang ### | ||||||
| /vendor/ | /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 | ci-pending-migrations: ci--meta-debug | ||||||
| 	ak makemigrations --check | 	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: |   postgresql: | ||||||
|     container_name: postgres |     container_name: postgres | ||||||
|     image: docker.io/library/postgres:16 |     image: docker.io/library/postgres:16 | ||||||
|  |     command: "-c max_connections=500" | ||||||
|     volumes: |     volumes: | ||||||
|       - db-data:/var/lib/postgresql/data |       - db-data:/var/lib/postgresql/data | ||||||
|     environment: |     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
	