From e399481a829b0a68dcc716a7592678060ac886aa Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Mon, 3 Aug 2026 14:38:05 +0100 Subject: [PATCH 01/13] Stub SM load_session. --- src/drunc/session_manager/session_manager.py | 13 ++++++++++ .../session_manager/session_manager_driver.py | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index 4bae2f1d0..271430045 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -120,6 +120,19 @@ def list_all_sessions( flag=ResponseFlag.EXECUTED_SUCCESSFULLY, ) + def load_session(self, request: Request, context: ServicerContext) -> None: + """Load a session based on the provided configuration key. + + Args: + request: The incoming request containing the configuration key. + context: The gRPC context (not used). + + Raises: + NotImplementedError: This method is not yet implemented. + """ + self.log.debug(f"{self.name} running load_session") + raise NotImplementedError("load_session is not yet implemented.") + def list_all_configs( self, request: Request, context: ServicerContext ) -> AllConfigKeys: diff --git a/src/drunc/session_manager/session_manager_driver.py b/src/drunc/session_manager/session_manager_driver.py index 073aef10f..d4613e3f4 100644 --- a/src/drunc/session_manager/session_manager_driver.py +++ b/src/drunc/session_manager/session_manager_driver.py @@ -102,6 +102,32 @@ def list_all_sessions(self, timeout: int | float = 60) -> AllActiveSessions: return response + def load_session(self, config_key: str, timeout: int | float = 60) -> None: + """Load a session based on the provided configuration key. + + Args: + config_key: The configuration key for the session to load. + timeout: The timeout for the gRPC call in seconds. + """ + # request = Request(token=copy_token(self.token)) + self.log.info( + f"Sending load_session request to session manager at {self.address} with timeout {timeout}s" + ) + + # try: + # self.stub.load_session(request, timeout=timeout) + # except grpc.RpcError as e: + # try: + # error_details = extract_grpc_rich_error(e) + # self.log.error(error_details) + # except Exception as extraction_error: + # self.log.debug( + # f"Could not extract rich error details from gRPC error: {extraction_error}", + # exc_info=True, + # ) + + # handle_grpc_error(e) + def list_all_configs(self, timeout: int | float = 60) -> AllConfigKeys: """List all available configurations in the session manager. From 36caf15aa43e2828db65d6817e558d2cf364c6dd Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Mon, 3 Aug 2026 14:51:49 +0100 Subject: [PATCH 02/13] Stub SM load_session. --- src/drunc/session_manager/session_manager.py | 26 +++++----- .../session_manager/session_manager_driver.py | 52 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index 271430045..6deb2def6 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -120,19 +120,6 @@ def list_all_sessions( flag=ResponseFlag.EXECUTED_SUCCESSFULLY, ) - def load_session(self, request: Request, context: ServicerContext) -> None: - """Load a session based on the provided configuration key. - - Args: - request: The incoming request containing the configuration key. - context: The gRPC context (not used). - - Raises: - NotImplementedError: This method is not yet implemented. - """ - self.log.debug(f"{self.name} running load_session") - raise NotImplementedError("load_session is not yet implemented.") - def list_all_configs( self, request: Request, context: ServicerContext ) -> AllConfigKeys: @@ -201,3 +188,16 @@ def list_all_configs( config_keys=configs, flag=ResponseFlag.EXECUTED_SUCCESSFULLY, ) + + def load_session(self, request: Request, context: ServicerContext) -> None: + """Load a session based on the provided configuration key. + + Args: + request: The incoming request containing the configuration key. + context: The gRPC context (not used). + + Raises: + NotImplementedError: This method is not yet implemented. + """ + self.log.debug(f"{self.name} running load_session") + raise NotImplementedError("load_session is not yet implemented.") diff --git a/src/drunc/session_manager/session_manager_driver.py b/src/drunc/session_manager/session_manager_driver.py index d4613e3f4..748b5b700 100644 --- a/src/drunc/session_manager/session_manager_driver.py +++ b/src/drunc/session_manager/session_manager_driver.py @@ -102,32 +102,6 @@ def list_all_sessions(self, timeout: int | float = 60) -> AllActiveSessions: return response - def load_session(self, config_key: str, timeout: int | float = 60) -> None: - """Load a session based on the provided configuration key. - - Args: - config_key: The configuration key for the session to load. - timeout: The timeout for the gRPC call in seconds. - """ - # request = Request(token=copy_token(self.token)) - self.log.info( - f"Sending load_session request to session manager at {self.address} with timeout {timeout}s" - ) - - # try: - # self.stub.load_session(request, timeout=timeout) - # except grpc.RpcError as e: - # try: - # error_details = extract_grpc_rich_error(e) - # self.log.error(error_details) - # except Exception as extraction_error: - # self.log.debug( - # f"Could not extract rich error details from gRPC error: {extraction_error}", - # exc_info=True, - # ) - - # handle_grpc_error(e) - def list_all_configs(self, timeout: int | float = 60) -> AllConfigKeys: """List all available configurations in the session manager. @@ -159,3 +133,29 @@ def list_all_configs(self, timeout: int | float = 60) -> AllConfigKeys: handle_grpc_error(e) return response + + def load_session(self, config_key: str, timeout: int | float = 60) -> None: + """Load a session based on the provided configuration key. + + Args: + config_key: The configuration key for the session to load. + timeout: The timeout for the gRPC call in seconds. + """ + # request = Request(token=copy_token(self.token)) + self.log.info( + f"Sending load_session request to session manager at {self.address} with timeout {timeout}s" + ) + + # try: + # self.stub.load_session(request, timeout=timeout) + # except grpc.RpcError as e: + # try: + # error_details = extract_grpc_rich_error(e) + # self.log.error(error_details) + # except Exception as extraction_error: + # self.log.debug( + # f"Could not extract rich error details from gRPC error: {extraction_error}", + # exc_info=True, + # ) + + # handle_grpc_error(e) From 6da115742f7d8c10fc5e83da4b51b7ab8a1b419a Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Mon, 3 Aug 2026 14:52:56 +0100 Subject: [PATCH 03/13] Add click command. --- src/drunc/session_manager/interface/commands.py | 9 +++++++++ src/drunc/session_manager/interface/shell.py | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/drunc/session_manager/interface/commands.py b/src/drunc/session_manager/interface/commands.py index 027ef10cd..f8d38e95b 100644 --- a/src/drunc/session_manager/interface/commands.py +++ b/src/drunc/session_manager/interface/commands.py @@ -23,3 +23,12 @@ def list_all_configs(ctx) -> None: """List all available configurations.""" response = ctx.obj.get_driver("session_manager").list_all_configs() click.echo(response) + + +@click.command("load_session") +@click.argument("config_key", type=str) +@click.pass_context +def load_session(ctx, config_key: str) -> None: + """Load a session based on the provided configuration key.""" + response = ctx.obj.get_driver("session_manager").load_session(config_key) + click.echo(response) diff --git a/src/drunc/session_manager/interface/shell.py b/src/drunc/session_manager/interface/shell.py index 77a66dd0a..466e890e9 100644 --- a/src/drunc/session_manager/interface/shell.py +++ b/src/drunc/session_manager/interface/shell.py @@ -8,6 +8,7 @@ describe, list_all_configs, list_all_sessions, + load_session, ) from drunc.utils.utils import ( CONTEXT_SETTINGS, @@ -42,6 +43,7 @@ def cleanup() -> None: ctx.command.add_command(describe, "describe") ctx.command.add_command(list_all_sessions, "list_all_sessions") ctx.command.add_command(list_all_configs, "list_all_configs") + ctx.command.add_command(load_session, "load_session") ctx.call_on_close(cleanup) log.info("Starting session manager shell") From 9d9c9871d587afb9418e576a181f49902e1e4ccc Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Mon, 3 Aug 2026 17:22:08 +0100 Subject: [PATCH 04/13] load_session: pass our file and session id over grpc. --- .../session_manager/interface/commands.py | 9 ++-- src/drunc/session_manager/session_manager.py | 17 +++++-- .../session_manager/session_manager_driver.py | 51 ++++++++++++------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/drunc/session_manager/interface/commands.py b/src/drunc/session_manager/interface/commands.py index f8d38e95b..95edcfb01 100644 --- a/src/drunc/session_manager/interface/commands.py +++ b/src/drunc/session_manager/interface/commands.py @@ -26,9 +26,10 @@ def list_all_configs(ctx) -> None: @click.command("load_session") -@click.argument("config_key", type=str) +@click.argument("file", type=str) +@click.argument("session_id", type=str) @click.pass_context -def load_session(ctx, config_key: str) -> None: - """Load a session based on the provided configuration key.""" - response = ctx.obj.get_driver("session_manager").load_session(config_key) +def load_session(ctx, file: str, session_id: str) -> None: + """Load a session based on the provided configuration file path and ID.""" + response = ctx.obj.get_driver("session_manager").load_session(file, session_id) click.echo(response) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index 6deb2def6..cd18ab74d 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -15,6 +15,8 @@ AllActiveSessions, AllConfigKeys, ConfigKey, + LoadSessionRequest, + LoadSessionResponse, ) from druncschema.session_manager_pb2_grpc import SessionManagerServicer from grpc import ServicerContext @@ -189,15 +191,22 @@ def list_all_configs( flag=ResponseFlag.EXECUTED_SUCCESSFULLY, ) - def load_session(self, request: Request, context: ServicerContext) -> None: + def load_session( + self, request: LoadSessionRequest, context: ServicerContext + ) -> LoadSessionResponse: """Load a session based on the provided configuration key. Args: request: The incoming request containing the configuration key. context: The gRPC context (not used). - Raises: - NotImplementedError: This method is not yet implemented. + Returns: + LoadSessionResponse: A response containing a result message. """ self.log.debug(f"{self.name} running load_session") - raise NotImplementedError("load_session is not yet implemented.") + + file = request.config_key.file + session_id = request.config_key.session_id + self.log.info(f"Loading session: '{session_id}' from file: '{file}'") + + return LoadSessionResponse(name=self.name) diff --git a/src/drunc/session_manager/session_manager_driver.py b/src/drunc/session_manager/session_manager_driver.py index 748b5b700..b038563fc 100644 --- a/src/drunc/session_manager/session_manager_driver.py +++ b/src/drunc/session_manager/session_manager_driver.py @@ -3,7 +3,13 @@ import grpc from druncschema.description_pb2 import Description from druncschema.request_response_pb2 import Request -from druncschema.session_manager_pb2 import AllActiveSessions, AllConfigKeys +from druncschema.session_manager_pb2 import ( + AllActiveSessions, + AllConfigKeys, + ConfigKey, + LoadSessionRequest, + LoadSessionResponse, +) from druncschema.session_manager_pb2_grpc import SessionManagerStub from druncschema.token_pb2 import Token @@ -134,28 +140,39 @@ def list_all_configs(self, timeout: int | float = 60) -> AllConfigKeys: return response - def load_session(self, config_key: str, timeout: int | float = 60) -> None: + def load_session( + self, file: str, session_id: str, timeout: int | float = 60 + ) -> LoadSessionResponse: """Load a session based on the provided configuration key. Args: - config_key: The configuration key for the session to load. + file: The file containing the session to load. + session_id: The ID of the session to load. timeout: The timeout for the gRPC call in seconds. """ - # request = Request(token=copy_token(self.token)) + request = LoadSessionRequest( + config_key=ConfigKey( + file=file, + session_id=session_id, + ) + ) + self.log.info( f"Sending load_session request to session manager at {self.address} with timeout {timeout}s" ) - # try: - # self.stub.load_session(request, timeout=timeout) - # except grpc.RpcError as e: - # try: - # error_details = extract_grpc_rich_error(e) - # self.log.error(error_details) - # except Exception as extraction_error: - # self.log.debug( - # f"Could not extract rich error details from gRPC error: {extraction_error}", - # exc_info=True, - # ) - - # handle_grpc_error(e) + try: + response = self.stub.load_session(request, timeout=timeout) + except grpc.RpcError as e: + try: + error_details = extract_grpc_rich_error(e) + self.log.error(error_details) + except Exception as extraction_error: + self.log.debug( + f"Could not extract rich error details from gRPC error: {extraction_error}", + exc_info=True, + ) + + handle_grpc_error(e) + + return response From d36493bb11b2333a1c2bf47b161d57bc8fca4add Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Tue, 4 Aug 2026 10:14:56 +0100 Subject: [PATCH 05/13] Add command description. --- src/drunc/session_manager/session_manager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index cd18ab74d..55d0c2e6f 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -80,6 +80,12 @@ def describe(self, request: Request, context: ServicerContext) -> Description: help="List all available configurations.", return_type="session_manager_pb2.AllConfigKeys", ), + CommandDescription( + name="load_session", + data_type=["session_manager_pb2.LoadSessionRequest"], + help="Load a session based on the provided configuration key.", + return_type="session_manager_pb2.LoadSessionResponse", + ), ] return Description( From 66f20d5fd7b98d919dd281ed4608513d7eff7923 Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Tue, 4 Aug 2026 12:08:49 +0100 Subject: [PATCH 06/13] Make list_all_sessions return actual sessions. --- src/drunc/session_manager/session_manager.py | 25 ++++++++------------ 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index 55d0c2e6f..3084cc1d9 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -3,6 +3,7 @@ import abc from os import getenv from pathlib import Path +from threading import Lock from conffwk import Configuration from druncschema.description_pb2 import CommandDescription, Description @@ -42,12 +43,14 @@ def __init__(self, name: str, configuration: SessionManagerConfHandler): """ super().__init__() - self.log = get_logger("session_manager", rich_handler=True) - self.log.debug(pid_info_str()) - self.log.debug("Initialized SessionManager") - self.name = name self.configuration = configuration + self._active_sessions: dict[str, ActiveSession] = {} + self._active_sessions_lock = Lock() + + self.log = get_logger("session_manager", rich_handler=True) + self.log.debug(pid_info_str()) + self.log.debug("Initialised session manager") def describe(self, request: Request, context: ServicerContext) -> Description: """Respond with a description of this session manager service. @@ -110,21 +113,13 @@ def list_all_sessions( """ self.log.debug(f"{self.name} running list_all_sessions") - dummy_config = ConfigKey( - file="dummy_config_file", - session_id="dummy_config_session_id", - ) - - dummy_session = ActiveSession( - name="dummy_session", - user="dummy_user", - config_key=dummy_config, - ) + with self._active_sessions_lock: + active_sessions = list(self._active_sessions.values()) return AllActiveSessions( name=self.name, token=None, - active_sessions=[dummy_session], + active_sessions=active_sessions, flag=ResponseFlag.EXECUTED_SUCCESSFULLY, ) From cb03136b0aeaba2dab2cfeb0554fb29db9055fb0 Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Tue, 4 Aug 2026 13:58:46 +0100 Subject: [PATCH 07/13] Make load_session arguments required options. --- .../session_manager/interface/commands.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/drunc/session_manager/interface/commands.py b/src/drunc/session_manager/interface/commands.py index 95edcfb01..d623b0ecc 100644 --- a/src/drunc/session_manager/interface/commands.py +++ b/src/drunc/session_manager/interface/commands.py @@ -26,10 +26,24 @@ def list_all_configs(ctx) -> None: @click.command("load_session") -@click.argument("file", type=str) -@click.argument("session_id", type=str) +@click.option( + "--session-file", + "-f", + type=str, + required=True, + help="The file containing the session to load.", +) +@click.option( + "--session-id", + "-i", + type=str, + required=True, + help="The ID of the session to load.", +) @click.pass_context -def load_session(ctx, file: str, session_id: str) -> None: +def load_session(ctx, session_file: str, session_id: str) -> None: """Load a session based on the provided configuration file path and ID.""" - response = ctx.obj.get_driver("session_manager").load_session(file, session_id) + response = ctx.obj.get_driver("session_manager").load_session( + session_file, session_id + ) click.echo(response) From 83f04c8d645aa9e77e2514a252daf9035e35f9d3 Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Tue, 4 Aug 2026 14:04:11 +0100 Subject: [PATCH 08/13] Don't log timeout in SM driver. --- .../session_manager/session_manager_driver.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/drunc/session_manager/session_manager_driver.py b/src/drunc/session_manager/session_manager_driver.py index b038563fc..c3ca445da 100644 --- a/src/drunc/session_manager/session_manager_driver.py +++ b/src/drunc/session_manager/session_manager_driver.py @@ -55,10 +55,9 @@ def describe(self, timeout: int | float = 60) -> Description: Returns: A response containing the description of the service. """ + self.log.info(f"Sending describe request to session manager at {self.address}") + request = Request(token=copy_token(self.token)) - self.log.info( - f"Sending describe request to session manager at {self.address} with timeout {timeout}s" - ) try: response: Description = self.stub.describe(request, timeout=timeout) @@ -85,11 +84,12 @@ def list_all_sessions(self, timeout: int | float = 60) -> AllActiveSessions: Returns: A response containing a list of all active sessions. """ - request = Request(token=copy_token(self.token)) self.log.info( - f"Sending list_all_sessions request to session manager at {self.address} with timeout {timeout}s" + f"Sending list_all_sessions request to session manager at {self.address}" ) + request = Request(token=copy_token(self.token)) + try: response: AllActiveSessions = self.stub.list_all_sessions( request, timeout=timeout @@ -117,11 +117,12 @@ def list_all_configs(self, timeout: int | float = 60) -> AllConfigKeys: Returns: A response containing all available configuration keys. """ - request = Request(token=copy_token(self.token)) self.log.info( - f"Sending list_all_configs request to session manager at {self.address} with timeout {timeout}s" + f"Sending list_all_configs request to session manager at {self.address}" ) + request = Request(token=copy_token(self.token)) + try: response: AllConfigKeys = self.stub.list_all_configs( request, timeout=timeout @@ -150,6 +151,10 @@ def load_session( session_id: The ID of the session to load. timeout: The timeout for the gRPC call in seconds. """ + self.log.info( + f"Sending load_session request to session manager at {self.address}" + ) + request = LoadSessionRequest( config_key=ConfigKey( file=file, @@ -157,10 +162,6 @@ def load_session( ) ) - self.log.info( - f"Sending load_session request to session manager at {self.address} with timeout {timeout}s" - ) - try: response = self.stub.load_session(request, timeout=timeout) except grpc.RpcError as e: From 4cba1bad885a41613ef08e68a9b74f0995bc4bed Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Thu, 6 Aug 2026 13:20:51 +0100 Subject: [PATCH 09/13] load_session: return loaded session metadata. --- src/drunc/session_manager/session_manager.py | 6 ++++-- src/drunc/session_manager/session_manager_driver.py | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index 3084cc1d9..dff14d7ac 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -202,7 +202,7 @@ def load_session( context: The gRPC context (not used). Returns: - LoadSessionResponse: A response containing a result message. + LoadSessionResponse: A response containing loaded session metadata. """ self.log.debug(f"{self.name} running load_session") @@ -210,4 +210,6 @@ def load_session( session_id = request.config_key.session_id self.log.info(f"Loading session: '{session_id}' from file: '{file}'") - return LoadSessionResponse(name=self.name) + session = ActiveSession(name=session_id) + + return LoadSessionResponse(session=session) diff --git a/src/drunc/session_manager/session_manager_driver.py b/src/drunc/session_manager/session_manager_driver.py index c3ca445da..8df200540 100644 --- a/src/drunc/session_manager/session_manager_driver.py +++ b/src/drunc/session_manager/session_manager_driver.py @@ -150,6 +150,9 @@ def load_session( file: The file containing the session to load. session_id: The ID of the session to load. timeout: The timeout for the gRPC call in seconds. + + Returns: + LoadSessionResponse: A response containing loaded session metadata. """ self.log.info( f"Sending load_session request to session manager at {self.address}" From 80227c069b13d64997621da1863188f86f54159e Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Fri, 7 Aug 2026 14:22:20 +0100 Subject: [PATCH 10/13] load_session: save session metadata. --- src/drunc/session_manager/session_manager.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index dff14d7ac..a8ed1caa4 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -211,5 +211,17 @@ def load_session( self.log.info(f"Loading session: '{session_id}' from file: '{file}'") session = ActiveSession(name=session_id) + with self._active_sessions_lock: + if session_id in self._active_sessions: + raise DruncSetupException( + message="Unable to load session", + details=f"Session '{session_id}' already exists.", + ) + + # TODO: Implement the actual session loading logic here. + + # TODO: Store connection URL in `ActiveSession`. + + self._active_sessions[session_id] = session - return LoadSessionResponse(session=session) + return LoadSessionResponse(session=session) From 434070d304b08e72e76676c35affb8f1e4d2500f Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Fri, 7 Aug 2026 15:32:00 +0100 Subject: [PATCH 11/13] Fix failing sm.describe test. --- src/drunc/session_manager/session_manager.py | 2 +- tests/session_manager/conftest.py | 6 ++++++ tests/session_manager/test_session_manager_servicer.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index a8ed1caa4..e3b3d019a 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -85,7 +85,7 @@ def describe(self, request: Request, context: ServicerContext) -> Description: ), CommandDescription( name="load_session", - data_type=["session_manager_pb2.LoadSessionRequest"], + data_type=["None"], help="Load a session based on the provided configuration key.", return_type="session_manager_pb2.LoadSessionResponse", ), diff --git a/tests/session_manager/conftest.py b/tests/session_manager/conftest.py index a4f18185d..b9bc5d163 100644 --- a/tests/session_manager/conftest.py +++ b/tests/session_manager/conftest.py @@ -234,6 +234,12 @@ def command_description_list(): help="List all available configurations.", return_type="session_manager_pb2.AllConfigKeys", ), + CommandDescription( + name="load_session", + data_type=["None"], + help="Load a session based on the provided configuration key.", + return_type="session_manager_pb2.LoadSessionResponse", + ), ] diff --git a/tests/session_manager/test_session_manager_servicer.py b/tests/session_manager/test_session_manager_servicer.py index b7fe77282..ac424f77b 100644 --- a/tests/session_manager/test_session_manager_servicer.py +++ b/tests/session_manager/test_session_manager_servicer.py @@ -18,7 +18,7 @@ def test_describe( session_manager, mock_request, mock_context, command_description_list, mock_logger ): response = session_manager.describe(mock_request, mock_context) - mock_logger.debug.assert_any_call("Initialized SessionManager") + mock_logger.debug.assert_any_call("Initialised session manager") assert isinstance(response, Description) assert response.name == "dummy_name" From 888f7426b79d47ff92c0014f73d9991e323bf0a8 Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Fri, 7 Aug 2026 15:40:24 +0100 Subject: [PATCH 12/13] Fix failing sm.list_all_sessions test. --- tests/session_manager/test_session_manager_servicer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/session_manager/test_session_manager_servicer.py b/tests/session_manager/test_session_manager_servicer.py index ac424f77b..92148ca1a 100644 --- a/tests/session_manager/test_session_manager_servicer.py +++ b/tests/session_manager/test_session_manager_servicer.py @@ -35,6 +35,7 @@ def test_list_all_sessions(session_manager, mock_request, mock_context, mock_log name="dummy_session", user="dummy_user", config_key=mock_config ) + session_manager._active_sessions = {mock_config.session_id: mock_session} response = session_manager.list_all_sessions(mock_request, mock_context) mock_logger.debug.assert_any_call(f"{response.name} running list_all_sessions") From 1b9a7530965fc8c82e95547b3ae3c36dc7fa4e07 Mon Sep 17 00:00:00 2001 From: James Paul Turner Date: Fri, 7 Aug 2026 16:21:31 +0100 Subject: [PATCH 13/13] Add tests for sm.laod_session. --- src/drunc/session_manager/session_manager.py | 7 ++- .../test_session_manager_servicer.py | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/drunc/session_manager/session_manager.py b/src/drunc/session_manager/session_manager.py index e3b3d019a..fc5191f26 100644 --- a/src/drunc/session_manager/session_manager.py +++ b/src/drunc/session_manager/session_manager.py @@ -210,7 +210,10 @@ def load_session( session_id = request.config_key.session_id self.log.info(f"Loading session: '{session_id}' from file: '{file}'") - session = ActiveSession(name=session_id) + session = ActiveSession( + name="session_name", user="session_user", config_key=request.config_key + ) + with self._active_sessions_lock: if session_id in self._active_sessions: raise DruncSetupException( @@ -219,7 +222,7 @@ def load_session( ) # TODO: Implement the actual session loading logic here. - + # TODO: Set user name and session name (different from session ID). # TODO: Store connection URL in `ActiveSession`. self._active_sessions[session_id] = session diff --git a/tests/session_manager/test_session_manager_servicer.py b/tests/session_manager/test_session_manager_servicer.py index 92148ca1a..79d92a0aa 100644 --- a/tests/session_manager/test_session_manager_servicer.py +++ b/tests/session_manager/test_session_manager_servicer.py @@ -9,6 +9,8 @@ AllActiveSessions, AllConfigKeys, ConfigKey, + LoadSessionRequest, + LoadSessionResponse, ) from drunc.exceptions import DruncSetupException @@ -159,3 +161,48 @@ def test_list_all_configs_dals_missing( mock_logger.error.assert_any_call( "Failed to get DALs from mock_file_1.data.xml: DALs missing or invalid" ) + + +def test_load_session(session_manager, mock_context, mock_logger): + """ + Test loading a session with a given configuration key. + """ + session_file = "dummy_config_file" + session_id = "dummy_config_session_id" + session_name = "session_name" + session_user = "session_user" + + mock_config = ConfigKey(file=session_file, session_id=session_id) + mock_request = LoadSessionRequest(config_key=mock_config) + + response = session_manager.load_session(mock_request, mock_context) + session = session_manager._active_sessions[session_id] + mock_logger.debug.assert_any_call(f"{session_manager.name} running load_session") + + assert session.name == session_name + assert session.user == session_user + assert session.config_key == mock_config + + assert isinstance(response, LoadSessionResponse) + assert response.session.name == session_name + assert response.session.user == session_user + assert response.session.config_key == mock_config + + +def test_load_session_duplicate_id(session_manager, mock_context): + """ + Test loading a session with a duplicate session ID. + """ + session_file = "dummy_config_file" + session_id = "dummy_config_session_id" + + mock_config = ConfigKey(file=session_file, session_id=session_id) + mock_request = LoadSessionRequest(config_key=mock_config) + + session_manager.load_session(mock_request, mock_context) + with pytest.raises(DruncSetupException) as excinfo: + session_manager.load_session(mock_request, mock_context) + + assert "Unable to load session" in str(excinfo.value) + assert len(session_manager._active_sessions) == 1 + assert session_manager._active_sessions[session_id].config_key == mock_config