Skip to content

Commit cae60f6

Browse files
authored
Merge pull request #248 from jumpstarter-dev/unixportforward
Init UnixPortforwardAdapter
2 parents b5d3066 + 7296d2b commit cae60f6

File tree

9 files changed

+144
-20
lines changed

9 files changed

+144
-20
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Adapter Packages
2+
3+
```{toctree}
4+
network.md
5+
```
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Network adapters
2+
3+
Network adapters are for transforming network connections exposed by drivers
4+
5+
```{eval-rst}
6+
.. autoclass:: jumpstarter_driver_network.adapters.TcpPortforwardAdapter
7+
:members:
8+
```
9+
10+
```{eval-rst}
11+
.. autoclass:: jumpstarter_driver_network.adapters.UnixPortforwardAdapter
12+
:members:
13+
```
14+
15+
```{eval-rst}
16+
.. autoclass:: jumpstarter_driver_network.adapters.NovncAdapter
17+
:members:
18+
```
19+
20+
```{eval-rst}
21+
.. autoclass:: jumpstarter_driver_network.adapters.PexpectAdapter
22+
:members:
23+
```
24+
25+
```{eval-rst}
26+
.. autoclass:: jumpstarter_driver_network.adapters.FabricAdapter
27+
:members:
28+
```
29+
30+
## Examples
31+
```yaml
32+
export:
33+
tcp_port:
34+
type: "jumpstarter_driver_network.driver.TcpNetwork"
35+
config:
36+
host: localhost
37+
port: 80
38+
unix_socket:
39+
type: "jumpstarter_driver_network.driver.UnixNetwork"
40+
config:
41+
path: /tmp/test.sock
42+
```
43+
44+
Forward a remote TCP port to a local TCP port
45+
46+
```{testcode}
47+
# random port on localhost
48+
with TcpPortforwardAdapter(client.tcp_port) as addr:
49+
print(addr[0], addr[1]) # 127.0.0.1 38406
50+
51+
# specific address and port
52+
with TcpPortforwardAdapter(client.tcp_port, local_host="192.0.2.1", local_port=8080) as addr:
53+
print(addr[0], addr[1]) # 192.0.2.1 8080
54+
```
55+
56+
Forward a remote Unix domain socket to a local socket
57+
58+
```{testcode}
59+
with UnixPortforwardAdapter(client.unix_socket) as addr:
60+
print(addr) # /tmp/jumpstarter-w30wxu64/socket
61+
62+
# the type of the remote socket and the local one doesn't have to match
63+
# e.g. forward a remote Unix domain socket to a local TCP port
64+
with TcpPortforwardAdapter(client.unix_socket) as addr:
65+
print(addr[0], addr[1]) # 127.0.0.1 38406
66+
```
67+
68+
Connect to a remote TCP port with a web-based VNC client
69+
70+
```{testcode}
71+
with NovncAdapter(client.tcp_port) as url:
72+
print(url) # https://novnc.com/noVNC/vnc.html?autoconnect=1&reconnect=1&host=127.0.0.1&port=36459
73+
# open the url in browser to access the VNC client
74+
```
75+
76+
Interact with a remote TCP port as if it's a serial console
77+
78+
See [pexpect](https://pexpect.readthedocs.io/en/stable/api/fdpexpect.html) for API documentation
79+
80+
```{testcode}
81+
with PexpectAdapter(client.tcp_port) as expect:
82+
expect.expect("localhost login:")
83+
expect.send("root\n")
84+
expect.expect("Password:")
85+
expect.send("secret\n")
86+
```
87+
88+
Connect to a remote TCP port with the fabric SSH client
89+
90+
See [fabric](https://docs.fabfile.org/en/latest/api/connection.html#fabric.connection.Connection) for API documentation
91+
92+
```{testcode}
93+
with FabricAdapter(client=client.tcp_port, connect_kwargs={"password": "secret"}) as conn:
94+
conn.run("uname")
95+
```

docs/source/api-reference/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ This section provides details on the Jumpstarter core API and contrib drivers.
66
drivers.md
77
adapters.md
88
drivers/index.md
9+
adapters/index.md
910
```
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .fabric import FabricAdapter
22
from .novnc import NovncAdapter
33
from .pexpect import PexpectAdapter
4-
from .portforward import PortforwardAdapter
4+
from .portforward import TcpPortforwardAdapter, UnixPortforwardAdapter
55

6-
__all__ = ["FabricAdapter", "NovncAdapter", "PexpectAdapter", "PortforwardAdapter"]
6+
__all__ = ["FabricAdapter", "NovncAdapter", "PexpectAdapter", "TcpPortforwardAdapter", "UnixPortforwardAdapter"]

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/fabric.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
from fabric.config import Config
55
from fabric.connection import Connection
66

7-
from .portforward import PortforwardAdapter
7+
from .portforward import TcpPortforwardAdapter
88

99

1010
@dataclass(kw_only=True)
11-
class FabricAdapter(PortforwardAdapter):
11+
class FabricAdapter(TcpPortforwardAdapter):
1212
user: str | None = None
1313
config: Config | None = None
1414
forward_agent: bool | None = None

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/novnc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
from urllib.parse import urlencode, urlunparse
33

44
from ..streams import WebsocketServerStream
5-
from .portforward import PortforwardAdapter
5+
from .portforward import TcpPortforwardAdapter
66
from jumpstarter.streams import forward_stream
77

88

99
@dataclass(kw_only=True)
10-
class NovncAdapter(PortforwardAdapter):
10+
class NovncAdapter(TcpPortforwardAdapter):
1111
async def __aenter__(self):
1212
addr = await super().__aenter__()
1313
return urlunparse(

packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/pexpect.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
from pexpect.fdpexpect import fdspawn
55

6-
from .portforward import PortforwardAdapter
6+
from .portforward import TcpPortforwardAdapter
77

88

99
@dataclass(kw_only=True)
10-
class PexpectAdapter(PortforwardAdapter):
10+
class PexpectAdapter(TcpPortforwardAdapter):
1111
async def __aenter__(self):
1212
addr = await super().__aenter__()
1313

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
from dataclasses import dataclass
22

33
from jumpstarter.client.adapters import ClientAdapter
4-
from jumpstarter.common import TemporaryTcpListener
4+
from jumpstarter.common import TemporaryTcpListener, TemporaryUnixListener
55
from jumpstarter.streams import forward_stream
66

77

88
@dataclass(kw_only=True)
99
class PortforwardAdapter(ClientAdapter):
10+
method: str = "connect"
11+
12+
async def __aexit__(self, exc_type, exc_value, traceback):
13+
return await self.listener.__aexit__(exc_type, exc_value, traceback)
14+
15+
async def handler(self, conn):
16+
async with conn:
17+
async with self.client.stream_async(self.method) as stream:
18+
async with forward_stream(conn, stream):
19+
pass
20+
21+
22+
@dataclass(kw_only=True)
23+
class TcpPortforwardAdapter(PortforwardAdapter):
1024
local_host: str = "127.0.0.1"
1125
local_port: int = 0
12-
method: str = "connect"
1326

1427
async def __aenter__(self):
1528
self.listener = TemporaryTcpListener(
@@ -18,11 +31,10 @@ async def __aenter__(self):
1831

1932
return await self.listener.__aenter__()
2033

21-
async def __aexit__(self, exc_type, exc_value, traceback):
22-
return await self.listener.__aexit__(exc_type, exc_value, traceback)
2334

24-
async def handler(self, conn):
25-
async with conn:
26-
async with self.client.stream_async(self.method) as stream:
27-
async with forward_stream(conn, stream):
28-
pass
35+
@dataclass(kw_only=True)
36+
class UnixPortforwardAdapter(PortforwardAdapter):
37+
async def __aenter__(self):
38+
self.listener = TemporaryUnixListener(self.handler)
39+
40+
return await self.listener.__aenter__()

packages/jumpstarter-driver-network/jumpstarter_driver_network/driver_test.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pytest
77
from anyio.from_thread import start_blocking_portal
88

9-
from .adapters import PortforwardAdapter
9+
from .adapters import TcpPortforwardAdapter, UnixPortforwardAdapter
1010
from .driver import EchoNetwork, TcpNetwork, UdpNetwork, UnixNetwork
1111
from jumpstarter.common import TemporaryTcpListener, TemporaryUnixListener
1212
from jumpstarter.common.utils import serve
@@ -41,13 +41,24 @@ def test_tcp_network_portforward():
4141
with start_blocking_portal() as portal:
4242
with portal.wrap_async_context_manager(TemporaryTcpListener(echo_handler, local_host="127.0.0.1")) as inner:
4343
with serve(TcpNetwork(host=inner[0], port=inner[1])) as client:
44-
with PortforwardAdapter(client=client) as addr:
44+
with TcpPortforwardAdapter(client=client) as addr:
4545
stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4646
stream.connect(addr)
4747
stream.send(b"hello")
4848
assert stream.recv(5) == b"hello"
4949

5050

51+
def test_unix_network_portforward():
52+
with start_blocking_portal() as portal:
53+
with portal.wrap_async_context_manager(TemporaryUnixListener(echo_handler)) as inner:
54+
with serve(UnixNetwork(path=inner)) as client:
55+
with UnixPortforwardAdapter(client=client) as addr:
56+
stream = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
57+
stream.connect(str(addr))
58+
stream.send(b"hello")
59+
assert stream.recv(5) == b"hello"
60+
61+
5162
def test_udp_network():
5263
with serve(
5364
UdpNetwork(
@@ -90,7 +101,7 @@ def test_tcp_network_performance():
90101
stderr=subprocess.DEVNULL,
91102
)
92103

93-
with PortforwardAdapter(client=client) as addr:
104+
with TcpPortforwardAdapter(client=client) as addr:
94105
subprocess.run(
95106
[
96107
"iperf3",

0 commit comments

Comments
 (0)