Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DbusNetwork driver #245

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft

Add DbusNetwork driver #245

wants to merge 4 commits into from

Conversation

NickCao
Copy link
Collaborator

@NickCao NickCao commented Jan 29, 2025

Right now the driver proxies the dbus socket directly without filtering. We should incorporate https://github.com/flatpak/xdg-dbus-proxy for that functionality.

Summary by CodeRabbit

  • New Features

    • Added support for DBus network connections, including system and session bus connectivity.
    • Introduced new port forwarding adapters for TCP and Unix connections.
  • Tests

    • Enhanced test coverage for DBus network system and session bus connections.
    • Introduced conditional tests to validate DBus network functionality.
    • Updated existing tests to utilize the new port forwarding adapters.

Copy link
Contributor

coderabbitai bot commented Jan 29, 2025

Walkthrough

The pull request introduces a new DbusNetwork class in the network driver package, extending NetworkInterface and Driver. This class provides a mechanism to establish connections to either system or session D-Bus buses. The implementation includes robust connection handling with support for different bus address schemes (unix and tcp), comprehensive error checking, and environment variable-based configuration. Corresponding test cases have been added to validate the new network driver's functionality across system and session bus scenarios.

Changes

File Changes
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py - Added new DbusNetwork class
- Implemented connect method with D-Bus connection logic
- Supports system and session bus types
- Handles unix and tcp bus address schemes
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py - Imported DbusNetwork
- Added test_dbus_network_system test function
- Added test_dbus_network_session test function
- Updated tests to use TcpPortforwardAdapter and UnixPortforwardAdapter
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/__init__.py - Replaced PortforwardAdapter with TcpPortforwardAdapter and UnixPortforwardAdapter in imports and __all__ declaration
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/fabric.py - Changed inheritance from PortforwardAdapter to TcpPortforwardAdapter
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/novnc.py - Changed inheritance from PortforwardAdapter to TcpPortforwardAdapter
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/pexpect.py - Changed inheritance from PortforwardAdapter to TcpPortforwardAdapter
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/portforward.py - Added TcpPortforwardAdapter and UnixPortforwardAdapter classes
- Updated methods inherited from PortforwardAdapter

Sequence Diagram

sequenceDiagram
    participant DN as DbusNetwork
    participant Env as Environment
    participant Bus as D-Bus Service

    DN->>Env: Retrieve bus address
    alt System Bus
        DN->>Env: Get DBUS_SYSTEM_BUS_ADDRESS
    else Session Bus
        DN->>Env: Get DBUS_SESSION_BUS_ADDRESS
    end

    DN->>DN: Parse bus address
    DN->>Bus: Establish Connection
    alt Connection Successful
        Bus-->>DN: Connected
    else Connection Failed
        DN->>DN: Raise ValueError
    end
Loading

Poem

🐰 A Rabbit's Ode to D-Bus Connection 🚌
With whiskers twitching, code so neat,
DBus buses, now a network treat!
System or session, connections fly,
Through unix and tcp, our driver's high!
Leaping forward, with network might! 🚀

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@NickCao
Copy link
Collaborator Author

NickCao commented Jan 29, 2025

This closes #152 and #153 as native client cli/libraries for bluez/iwd/wpa_supplicant can now be used as usual remotely via the forwarded dbus socket.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)

66-69: Add docstring for DbusNetwork class.
It would be beneficial to add a docstring to briefly explain how the bus type is determined (system vs session) and how environment variables are leveraged. This helps future maintainers understand the intended usage.

packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (1)

113-116: Ensure tests run in a controlled environment.
Skipping the test if the DBus system bus isn't present is appropriate. Consider adding a fallback or mocking approach if you want coverage in CI environments that lack system D-Bus.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ded89ae and d4aa8e1.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (2 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: pytest-matrix (3.13)
  • GitHub Check: pytest-matrix (3.11)
  • GitHub Check: e2e
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter-devspace .devfile/Containerfile.client)
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter Dockerfile)
🔇 Additional comments (3)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)

4-5: Use caution when mixing getenv and direct environment variable references.
While it's perfectly fine to import and use getenv and getuid here, consider providing more context in docstrings or in your class docstring to clarify how you expect environment variables to be set for bus addresses.
[approve]

packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (2)

11-11: Imports look good.
All essential classes are imported. No issues.


118-128: Confirm output validation.
Currently, the test uses subprocess.run with check=True but doesn't assert anything about the output. Confirm if you need to verify the output content to ensure correctness.

Comment on lines 70 to 119
@exportstream
@asynccontextmanager
async def connect(self): # noqa: C901
match self.kind:
case "system":
bus = getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
case "session":
bus = getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
case _:
raise ValueError(f"invalid bus type: {self.kind}")

scheme, sep, rem = bus.partition(":")
if not sep:
raise ValueError(f"invalid bus addr: {bus}")

args = {}
for part in rem.split(","):
key, sep, value = part.partition("=")
if not sep:
raise ValueError(f"invalid bus addr: {bus}, missing separator in arguments")
args[key] = value

match scheme:
case "unix":
if "path" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing path argument")

self.logger.debug("Connecting UDS path=%s", args["path"])
async with await connect_unix(path=args["path"]) as stream:
yield stream
case "tcp":
if "host" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing host argument")
if "port" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing port argument")

try:
port = int(args["port"])
except ValueError as e:
raise ValueError(f"invalid bus addr: {bus}, invalid port argument") from e

self.logger.debug("Connecting TCP host=%s port=%d", args["host"], port)
async with await connect_tcp(remote_host=args["host"], remote_port=port) as stream:
yield stream
case _:
raise ValueError(f"invalid bus scheme: {scheme}")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider breaking down the logic to reduce complexity.
The match statement plus bus parsing in connect is somewhat lengthy. Extracting the bus parsing into a helper function or methods for each scheme (unix vs tcp) might improve readability and maintainability.

 async def connect(self):  # noqa: C901
     bus = self._get_bus_address()  # extracted logic
     scheme, sep, rem = bus.partition(":")
     ...
+
+def _get_bus_address(self) -> str:
+    match self.kind:
+        case "system":
+            return getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
+        case "session":
+            return getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
+        case _:
+            raise ValueError(f"invalid bus type: {self.kind}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@exportstream
@asynccontextmanager
async def connect(self): # noqa: C901
match self.kind:
case "system":
bus = getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
case "session":
bus = getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
case _:
raise ValueError(f"invalid bus type: {self.kind}")
scheme, sep, rem = bus.partition(":")
if not sep:
raise ValueError(f"invalid bus addr: {bus}")
args = {}
for part in rem.split(","):
key, sep, value = part.partition("=")
if not sep:
raise ValueError(f"invalid bus addr: {bus}, missing separator in arguments")
args[key] = value
match scheme:
case "unix":
if "path" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing path argument")
self.logger.debug("Connecting UDS path=%s", args["path"])
async with await connect_unix(path=args["path"]) as stream:
yield stream
case "tcp":
if "host" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing host argument")
if "port" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing port argument")
try:
port = int(args["port"])
except ValueError as e:
raise ValueError(f"invalid bus addr: {bus}, invalid port argument") from e
self.logger.debug("Connecting TCP host=%s port=%d", args["host"], port)
async with await connect_tcp(remote_host=args["host"], remote_port=port) as stream:
yield stream
case _:
raise ValueError(f"invalid bus scheme: {scheme}")
@exportstream
@asynccontextmanager
async def connect(self): # noqa: C901
bus = self._get_bus_address()
scheme, sep, rem = bus.partition(":")
if not sep:
raise ValueError(f"invalid bus addr: {bus}")
args = {}
for part in rem.split(","):
key, sep, value = part.partition("=")
if not sep:
raise ValueError(f"invalid bus addr: {bus}, missing separator in arguments")
args[key] = value
match scheme:
case "unix":
if "path" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing path argument")
self.logger.debug("Connecting UDS path=%s", args["path"])
async with await connect_unix(path=args["path"]) as stream:
yield stream
case "tcp":
if "host" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing host argument")
if "port" not in args:
raise ValueError(f"invalid bus addr: {bus}, missing port argument")
try:
port = int(args["port"])
except ValueError as e:
raise ValueError(f"invalid bus addr: {bus}, invalid port argument") from e
self.logger.debug("Connecting TCP host=%s port=%d", args["host"], port)
async with await connect_tcp(remote_host=args["host"], remote_port=port) as stream:
yield stream
case _:
raise ValueError(f"invalid bus scheme: {scheme}")
def _get_bus_address(self) -> str:
match self.kind:
case "system":
return getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
case "session":
return getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
case _:
raise ValueError(f"invalid bus type: {self.kind}")

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (1)

113-144: Consider enhancing test coverage.

While the basic connectivity tests are good, consider adding:

  1. Docstrings explaining the purpose of each test
  2. Verification of the busctl output to ensure expected services are available
  3. Error scenario tests (e.g., invalid bus kind, connection failures)
  4. Tests for actual D-Bus service functionality (e.g., calling a method on a known service)

Example enhancement for system bus test:

 @pytest.mark.skipif(
     os.getenv("DBUS_SYSTEM_BUS_ADDRESS") is None and not os.path.exists("/run/dbus/system_bus_socket"),
     reason="dbus system bus not available",
 )
 @pytest.mark.skipif(which("busctl") is None, reason="busctl not available")
 def test_dbus_network_system(monkeypatch):
+    """
+    Test D-Bus system bus connectivity and basic functionality.
+    
+    Verifies:
+    1. Connection to system bus can be established
+    2. Basic D-Bus operations work through the forwarded connection
+    """
     with serve(DbusNetwork(kind="system")) as client:
         with PortforwardAdapter(client=client) as addr:
-            subprocess.run(
+            result = subprocess.run(
                 ["busctl", "list", "--system", "--no-pager"],
                 env=os.environ | {"DBUS_SYSTEM_BUS_ADDRESS": f"tcp:host={addr[0]},port={addr[1]}"},
                 check=True,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
             )
+            # Verify expected system services are present
+            output = result.stdout.decode()
+            assert "org.freedesktop.DBus" in output, "D-Bus service not found"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4aa8e1 and 52fc8c4.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: pytest-matrix (3.13)
  • GitHub Check: pytest-matrix (3.12)
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (3.11)
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter-devspace .devfile/Containerfile.client)
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter-dev .devfile/Containerfile)
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter Dockerfile)
🔇 Additional comments (3)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (3)

11-11: LGTM!

The import of DbusNetwork is correctly placed alongside other network-related imports.


113-128: LGTM! Well-structured test for D-Bus system bus.

The test correctly verifies basic connectivity to the D-Bus system bus with appropriate skip conditions and environment configuration.


130-144: LGTM! Well-structured test for D-Bus session bus.

The test correctly verifies basic connectivity to the D-Bus session bus with appropriate skip conditions and environment configuration.

@mangelajo
Copy link
Member

This closes #152 and #153 as native client cli/libraries for bluez/iwd/wpa_supplicant can now be used as usual remotely via the forwarded dbus socket.

It would not close those issues, but make it much easier to develop. To close those issues we may have to offer extensive documentation and wrappers on top to let do the specific actions on the client side.

@mangelajo
Copy link
Member

Please add documentation with examples of usage, exporter configuration, etc...

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/portforward.py (1)

22-33: Consider adding validation for local_host and local_port.

While the implementation is correct, it would be beneficial to add input validation for local_host and local_port to prevent potential runtime errors.

 @dataclass(kw_only=True)
 class TcpPortforwardAdapter(PortforwardAdapter):
     local_host: str = "127.0.0.1"
     local_port: int = 0
+
+    def __post_init__(self):
+        if not isinstance(self.local_port, int) or self.local_port < 0:
+            raise ValueError(f"Invalid port number: {self.local_port}")
+        if not self.local_host:
+            raise ValueError("local_host cannot be empty")
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (1)

124-139: Consider adding more test cases for D-Bus system bus.

While the basic functionality is tested, consider adding test cases for:

  • Error cases (invalid bus address)
  • Connection failures
  • Different message types
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52fc8c4 and dbac250.

📒 Files selected for processing (7)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/__init__.py (1 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/fabric.py (1 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/novnc.py (1 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/pexpect.py (1 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/portforward.py (2 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (3 hunks)
  • packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: pytest-matrix (3.13)
  • GitHub Check: pytest-matrix (3.12)
  • GitHub Check: e2e
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter-devspace .devfile/Containerfile.client)
  • GitHub Check: pytest-matrix (3.11)
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter-dev .devfile/Containerfile)
  • GitHub Check: build-and-push-image (jumpstarter-dev/jumpstarter Dockerfile)
🔇 Additional comments (9)
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/__init__.py (1)

4-6: LGTM! Clean refactoring of port forwarding adapters.

The changes properly expose the new specialized TCP and Unix socket forwarding adapters while maintaining backward compatibility through the __all__ declaration.

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/pexpect.py (1)

6-6: LGTM! Appropriate use of TCP-specific adapter.

The change to inherit from TcpPortforwardAdapter aligns with the class's use of TCP sockets (AF_INET) for communication.

Also applies to: 10-10

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/fabric.py (1)

7-7: LGTM! Appropriate use of TCP-specific adapter.

The change to inherit from TcpPortforwardAdapter aligns with the class's use of TCP-specific connection parameters (host and port).

Also applies to: 11-11

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/novnc.py (1)

5-5: LGTM! Appropriate use of TCP-specific adapter.

The change to inherit from TcpPortforwardAdapter aligns with:

  1. The class's use of TCP-specific connection parameters (host and port)
  2. WebSocket protocol operating over TCP

Also applies to: 10-10

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/portforward.py (2)

8-20: LGTM! Well-structured base adapter implementation.

The PortforwardAdapter provides a clean base implementation with proper connection lifecycle management and stream forwarding.


35-40: LGTM! Clean Unix adapter implementation.

The UnixPortforwardAdapter provides a straightforward implementation for Unix socket forwarding.

packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (2)

67-70: LGTM! Well-defined class with proper type constraints.

The use of Literal type for kind ensures type safety at compile time.


71-118: Consider breaking down the complex connection logic.

The connection method is quite complex with multiple responsibilities. Consider extracting the bus address parsing and connection logic into separate methods.

+    def _parse_bus_address(self) -> tuple[str, dict[str, str]]:
+        match self.kind:
+            case "system":
+                bus = getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
+            case "session":
+                bus = getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
+            case _:
+                raise ValueError(f"invalid bus type: {self.kind}")
+
+        scheme, sep, rem = bus.partition(":")
+        if not sep:
+            raise ValueError(f"invalid bus addr: {bus}")
+
+        args = {}
+        for part in rem.split(","):
+            key, sep, value = part.partition("=")
+            if not sep:
+                raise ValueError(f"invalid bus addr: {bus}, missing separator in arguments")
+            args[key] = value
+
+        return scheme, args

+    async def _connect_unix(self, path: str) -> MetadataStream:
+        self.logger.debug("Connecting UDS path=%s", path)
+        stream = await connect_unix(path=path)
+        return MetadataStream(stream=stream, metadata={"kind": self.kind})

+    async def _connect_tcp(self, host: str, port: int) -> MetadataStream:
+        self.logger.debug("Connecting TCP host=%s port=%d", host, port)
+        stream = await connect_tcp(remote_host=host, remote_port=port)
+        return MetadataStream(stream=stream, metadata={"kind": self.kind})

     @exportstream
     @asynccontextmanager
     async def connect(self):  # noqa: C901
-        match self.kind:
-            case "system":
-                bus = getenv("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
-            case "session":
-                bus = getenv("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
-            case _:
-                raise ValueError(f"invalid bus type: {self.kind}")
-
-        scheme, sep, rem = bus.partition(":")
-        if not sep:
-            raise ValueError(f"invalid bus addr: {bus}")
-
-        args = {}
-        for part in rem.split(","):
-            key, sep, value = part.partition("=")
-            if not sep:
-                raise ValueError(f"invalid bus addr: {bus}, missing separator in arguments")
-            args[key] = value
+        scheme, args = self._parse_bus_address()

         match scheme:
             case "unix":
                 if "path" not in args:
                     raise ValueError(f"invalid bus addr: {bus}, missing path argument")
-
-                self.logger.debug("Connecting UDS path=%s", args["path"])
-                async with await connect_unix(path=args["path"]) as stream:
-                    async with MetadataStream(stream=stream, metadata={"kind": self.kind}) as mstream:
+                async with await self._connect_unix(args["path"]) as mstream:
                         yield mstream
             case "tcp":
                 if "host" not in args:
                     raise ValueError(f"invalid bus addr: {bus}, missing host argument")
                 if "port" not in args:
                     raise ValueError(f"invalid bus addr: {bus}, missing port argument")

                 try:
                     port = int(args["port"])
                 except ValueError as e:
                     raise ValueError(f"invalid bus addr: {bus}, invalid port argument") from e

-                self.logger.debug("Connecting TCP host=%s port=%d", args["host"], port)
-                async with await connect_tcp(remote_host=args["host"], remote_port=port) as stream:
-                    async with MetadataStream(stream=stream, metadata={"kind": self.kind}) as mstream:
+                async with await self._connect_tcp(args["host"], port) as mstream:
                         yield mstream
             case _:
                 raise ValueError(f"invalid bus scheme: {scheme}")
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py (1)

141-155: Consider adding more test cases for D-Bus session bus.

Similar to the system bus tests, consider adding more test cases for comprehensive coverage.

Additionally, verify that the test environment is properly cleaned up:

@NickCao
Copy link
Collaborator Author

NickCao commented Feb 3, 2025

Blocked by our inability to pass labels from drivers to clients: we need a mechanism to pass the type of the dbus bus (session or system) to the client side, so it knows which environment variable to set.

@NickCao NickCao marked this pull request as draft February 3, 2025 17:33
@mangelajo
Copy link
Member

Blocked by our inability to pass labels from drivers to clients: we need a mechanism to pass the type of the dbus bus (session or system) to the client side, so it knows which environment variable to set.

What do you mean?

@NickCao
Copy link
Collaborator Author

NickCao commented Feb 3, 2025

What do you mean?

So the driver can proxy one of two different kinds of dbus: session bus or system bus, and they are represented by two different environment variables DBUS_SYSTEM_BUS_ADDRTESS or DBUS_SESSION_BUS_ADDRESS. The environment variable set on the client side should match the one used on the exporter/driver side, but we yet to have a way to pass that information to the client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants