Skip to content

Commit 921433e

Browse files
committed
add mass creation user, branch and proposed changes
1 parent 4d18625 commit 921433e

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed

utilities/infrahub_load_tester.py

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
"""Infrahub load test: bulk creation and deletion of users & branches, proposed changes."""
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
import random
7+
import string
8+
from typing import TYPE_CHECKING, Iterable
9+
10+
if TYPE_CHECKING:
11+
import logging
12+
13+
from infrahub_sdk import InfrahubClient
14+
15+
DEFAULT_N_USERS: int = 60
16+
DEFAULT_PREFIX: str = "loadtest"
17+
DEFAULT_PASSWORD: int = 16
18+
19+
20+
def _rand_pwd(length: int = DEFAULT_PASSWORD) -> str:
21+
"""Return a random alphanumeric password of *length* characters."""
22+
chars = string.ascii_letters + string.digits
23+
return "".join(random.choice(chars) for _ in range(length))
24+
25+
26+
async def _create_one(idx: int, client: InfrahubClient, prefix: str, log: logging.Logger) -> tuple[str, str]:
27+
"""Create one account, branch and diff; return `(username, branch)."""
28+
uname = f"{prefix}_user_{idx:02d}"
29+
pwd = _rand_pwd()
30+
31+
acc = await client.create("CoreAccount", name=uname, password=pwd, branch="main")
32+
await acc.save()
33+
34+
branch_name = f"{prefix}/{uname}"
35+
await client.branch.create(branch_name=branch_name)
36+
37+
tag = await client.create("BuiltinTag", name=f"{uname}-tag", branch=branch_name)
38+
await tag.save()
39+
40+
try:
41+
proposed_change = await client.create(
42+
"CoreProposedChange",
43+
data={"name": f"Proposed change for {uname}", "source_branch": branch_name, "destination_branch": "main"},
44+
)
45+
await proposed_change.save()
46+
log.info(f"✅ Created proposed change for branch {branch_name}")
47+
except Exception as e:
48+
log.error(f"❌ Error creating proposed change for branch {branch_name}: {e}")
49+
50+
log.info(f"✅ User {uname} created with branch {branch_name}")
51+
return uname, branch_name
52+
53+
54+
async def setup(client: InfrahubClient, log: logging.Logger, *, n_users: int, prefix: str) -> None:
55+
"""Create *n_users* users + branches in parallel."""
56+
tasks = [_create_one(i, client, prefix, log) for i in range(1, n_users + 1)]
57+
await asyncio.gather(*tasks, return_exceptions=False)
58+
59+
60+
def _generate_usernames(n_users: int, prefix: str) -> list[str]:
61+
"""Return the expected usernames produced during setup."""
62+
return [f"{prefix}_user_{i:02d}" for i in range(1, n_users + 1)]
63+
64+
65+
async def _delete_branches(client: InfrahubClient, prefix: str, usernames: Iterable[str], log: logging.Logger) -> None:
66+
"""Delete branches `<prefix>/<username> one by one."""
67+
try:
68+
all_branches = await client.branch.all()
69+
except Exception as e:
70+
log.error(f"Error retrieving branches: {e}")
71+
72+
for uname in usernames:
73+
br = f"{prefix}/{uname}"
74+
log.info(f"Attempting to delete branch {br}")
75+
try:
76+
branch_exists = br in all_branches
77+
log.info(f"Branch {br} exists: {branch_exists}")
78+
79+
if branch_exists:
80+
await client.branch.delete(br)
81+
log.info(f"🗑️ Branch {br} deleted")
82+
else:
83+
log.warning(f"Branch {br} not found")
84+
except Exception as exc:
85+
log.error(f"Error deleting branch {br}: {exc}")
86+
87+
88+
async def _delete_users(client: InfrahubClient, usernames: Iterable[str], log: logging.Logger) -> None:
89+
"""Delete CoreAccounts listed in *usernames*."""
90+
for uname in usernames:
91+
log.info(f"Trying to remove user {uname}")
92+
try:
93+
all_users = await client.all("CoreAccount", branch="main")
94+
for user in all_users:
95+
user_name = None
96+
if hasattr(user, "name"):
97+
if hasattr(user.name, "value"):
98+
user_name = user.name.value
99+
else:
100+
user_name = str(user.name)
101+
102+
if user_name == uname:
103+
log.info(f"Found! Removing {uname}")
104+
try:
105+
await user.delete()
106+
log.info(f"🗑️ User {uname} Deleted")
107+
break
108+
except Exception as e:
109+
log.error(f"Error while deleting: {str(e)}")
110+
else:
111+
log.warning(f"User {uname} not found in the list")
112+
113+
except Exception as exc:
114+
log.error(f"❌ General: {exc}")
115+
116+
117+
async def cleanup(client: InfrahubClient, log: logging.Logger, *, prefix: str, n_users: int) -> None:
118+
"""Remove test branches **and** test accounts."""
119+
usernames = _generate_usernames(n_users, prefix)
120+
await _delete_branches(client, prefix, usernames, log)
121+
await _delete_users(client, usernames, log)
122+
123+
124+
async def create_admin_branches(client: InfrahubClient, log: logging.Logger, *, n_branches: int, prefix: str) -> None:
125+
"""Create multiple branches for the admin user without creating new users."""
126+
log.info(f"Creating {n_branches} branches for admin user with prefix {prefix}")
127+
128+
for i in range(1, n_branches + 1):
129+
branch_name = f"{prefix}/admin_branch_{i:02d}"
130+
try:
131+
log.info(f"Creating branch {branch_name}")
132+
await client.branch.create(branch_name=branch_name)
133+
134+
tag = await client.create("BuiltinTag", name=f"admin-tag-{i:02d}", branch=branch_name)
135+
await tag.save()
136+
137+
log.info(f"✅ Branch created: {branch_name} with test tag")
138+
except Exception as exc:
139+
log.error(f"❌ Error creating branch {branch_name}: {exc}")
140+
141+
142+
async def delete_admin_branches(client: InfrahubClient, log: logging.Logger, *, n_branches: int, prefix: str) -> None:
143+
"""Delete branches created for the admin user."""
144+
log.info(f"Deleting {n_branches} admin branches with prefix {prefix}")
145+
146+
try:
147+
all_branches = await client.branch.all()
148+
except Exception as e:
149+
log.error(f"Error retrieving branches: {e}")
150+
return
151+
152+
for i in range(1, n_branches + 1):
153+
branch_name = f"{prefix}/admin_branch_{i:02d}"
154+
log.info(f"Attempting to delete branch {branch_name}")
155+
156+
try:
157+
branch_exists = branch_name in all_branches
158+
log.info(f"Branch {branch_name} exists: {branch_exists}")
159+
160+
if branch_exists:
161+
await client.branch.delete(branch_name)
162+
log.info(f"🗑️ Branch {branch_name} deleted")
163+
else:
164+
log.warning(f"Branch {branch_name} not found")
165+
except Exception as exc:
166+
log.error(f"Error deleting branch {branch_name}: {exc}")
167+
168+
169+
async def run(
170+
client: InfrahubClient,
171+
log: logging.Logger,
172+
branch: str = "main", # noqa: ARG001
173+
mode: str = "setup",
174+
n_users: int | str = DEFAULT_N_USERS,
175+
prefix: str = DEFAULT_PREFIX,
176+
) -> None:
177+
"""Main entrypoint executed by **infrahubctl run**."""
178+
179+
mode = str(mode).lower()
180+
prefix = str(prefix)
181+
n_users = int(n_users)
182+
183+
log.info("Mode: %s | Users/Branches: %s | Prefix: %s", mode, n_users, prefix)
184+
185+
if mode in {"setup", "create"}:
186+
await setup(client, log, n_users=n_users, prefix=prefix)
187+
elif mode in {"cleanup", "delete", "purge"}:
188+
await cleanup(client, log, prefix=prefix, n_users=n_users)
189+
elif mode == "admin_branches":
190+
await create_admin_branches(client, log, n_branches=n_users, prefix=prefix)
191+
elif mode == "delete_admin_branches":
192+
await delete_admin_branches(client, log, n_branches=n_users, prefix=prefix)
193+
else:
194+
log.error("Unknown mode: %s (expected: setup / cleanup / admin_branches / delete_admin_branches)", mode)

0 commit comments

Comments
 (0)