1313from dataclasses import dataclass
1414from playwright .async_api import async_playwright , Browser
1515
16+ @dataclass
17+ class Server :
18+ servername : str
19+ username : str
20+ hub_url : URL
21+
22+ @dataclass
23+ class RunningServer (Server ):
24+ start_request_time : datetime
25+ start_completion_time : datetime
26+ server_url : URL
27+ startup_events : List [dict ]
28+
29+ @dataclass
30+ class FailedServer (Server ):
31+ start_request_time : datetime
32+ start_failure_time : datetime
33+ startup_events : List [dict ]
1634
1735@dataclass
1836class ServerStartResult :
@@ -33,13 +51,13 @@ def __str__(self) -> str:
3351 return f"user:{ self .username } server:{ self .servername } started:{ self .started_successfully } startup_duration:{ self .startup_duration } "
3452
3553
36- async def load_nbgitpuller_url (browser : Browser , server_url : URL , token : str , nbgitpuller_url : URL , screenshot_name : str ):
37- print (f"visiting { server_url } " )
54+ async def load_nbgitpuller_url (browser : Browser , server : RunningServer , token : str , nbgitpuller_url : URL , screenshot_name : str ):
55+ print (f"visiting { server . server_url } " )
3856 nbgitpuller_url = nbgitpuller_url .extend_query ({
3957 'targetpath' : secrets .token_urlsafe (8 )
4058 })
41- target_url = (server_url / nbgitpuller_url .path ).with_query (nbgitpuller_url .query )
42- await_url = server_url / target_url .query .get ("urlpath" , "/lab" ).rstrip ("/" )
59+ target_url = (server . server_url / nbgitpuller_url .path ).with_query (nbgitpuller_url .query )
60+ await_url = server . server_url / target_url .query .get ("urlpath" , "/lab" ).rstrip ("/" )
4361 start_time = datetime .now ()
4462
4563 context = await browser .new_context (extra_http_headers = {
@@ -51,22 +69,22 @@ async def load_nbgitpuller_url(browser: Browser, server_url: URL, token: str, nb
5169 await page .wait_for_load_state ("networkidle" )
5270 await page .screenshot (path = screenshot_name )
5371 duration = datetime .now () - start_time
54- print (f"{ server_url } completed test in { duration } " )
72+ print (f"{ server . server_url } completed test in { duration } " )
5573
5674
5775
58- async def start_named_server (session : aiohttp .ClientSession , hub_url : URL , username : str , server_name : str ) -> ServerStartResult :
76+ async def start_named_server (session : aiohttp .ClientSession , server : Server ) -> RunningServer | None :
5977 """
6078 Try to start a named server as defined
6179
6280 """
63- server_api_url = hub_url / "hub/api/users" / username / "servers" / server_name
81+ server_api_url = server . hub_url / "hub/api/users" / server . username / "servers" / server . servername
6482 events = []
6583 async with session .post (server_api_url ) as resp :
6684 start_time = datetime .now ()
6785 if resp .status == 202 :
6886 # we are awaiting start, let's look for events
69- print (f"server { server_name } waiting to start" )
87+ print (f"server { server . servername } waiting to start" )
7088 async with session .get (server_api_url / "progress" ) as progress_resp :
7189 async for line in progress_resp .content :
7290 if line .decode ().strip () == '' :
@@ -76,14 +94,14 @@ async def start_named_server(session: aiohttp.ClientSession, hub_url: URL, usern
7694 events .append (progress_event )
7795 if progress_event .get ("ready" ) == True :
7896 print (progress_event )
79- return ServerStartResult (
80- username = username ,
81- servername = server_name ,
82- start_time = start_time ,
83- completion_time = datetime . now () ,
84- started_successfully = True ,
85- events = events ,
86- server_url = URL (hub_url / progress_event ['url' ][1 :]) # Trim leading slashG
97+ return RunningServer (
98+ servername = server . servername ,
99+ username = server . username ,
100+ hub_url = server . hub_url ,
101+ start_request_time = start_time ,
102+ start_completion_time = datetime . now () ,
103+ startup_events = events ,
104+ server_url = URL (server . hub_url / progress_event ['url' ][1 :]) # Trim leading slashG
87105 )
88106 elif resp .status == 201 :
89107 # Means the server is immediately ready, and i don't want to deal with that yet
@@ -93,6 +111,15 @@ async def start_named_server(session: aiohttp.ClientSession, hub_url: URL, usern
93111 resp .raise_for_status ()
94112
95113
114+ async def payload (session : aiohttp .ClientSession , browser : Browser , auth_token : str , nbgitpuller_url : URL , server : Server ):
115+ started_server = await start_named_server (session , server )
116+ match started_server :
117+ case RunningServer ():
118+ await load_nbgitpuller_url (browser , started_server , auth_token , nbgitpuller_url , server .servername + ".png" )
119+ case _:
120+ print ("Server startup failed" )
121+
122+
96123async def main ():
97124 argparser = argparse .ArgumentParser ()
98125 argparser .add_argument ("hub_url" , help = "Full URL to the JupyterHub to test against" )
@@ -112,14 +139,9 @@ async def main():
112139 async with aiohttp .ClientSession (headers = {
113140 "Authorization" : f"token { token } "
114141 }) as session :
115- servernames = [f"perf-test-{ i } " for i in range (args .servers_count )]
116- all_servers = []
117- async with aiometer .amap (partial (start_named_server , session , hub_url , args .username ), servernames , max_at_once = args .max_concurrency ) as servers :
118- async for server in servers :
119- all_servers .append (server )
120-
142+ servers_to_start = [Server (f"perf-{ i } " , args .username , hub_url ) for i in range (args .servers_count )]
121143 await aiometer .run_all (
122- [partial (load_nbgitpuller_url , browser , server . server_url , token , nbgitpuller_url , server . servername + ".png" ) for server in all_servers ],
144+ [partial (payload , session , browser , token , nbgitpuller_url , server ) for server in servers_to_start ],
123145 max_at_once = args .max_concurrency
124146 )
125147
0 commit comments