Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 14bf451

Browse files
authored
Merge pull request #393 from jumpstarter-dev/portforward-cli
Implement forward-{tcp,port} command on network client
2 parents 7b12041 + d980411 commit 14bf451

5 files changed

Lines changed: 70 additions & 6 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from contextlib import asynccontextmanager
22
from functools import partial
3+
from os import PathLike
34

45
from jumpstarter.client import DriverClient
56
from jumpstarter.client.adapters import blocking
@@ -37,6 +38,7 @@ async def UnixPortforwardAdapter(
3738
*,
3839
client: DriverClient,
3940
method: str = "connect",
41+
path: PathLike | None = None,
4042
):
41-
async with TemporaryUnixListener(partial(handler, client, method)) as addr:
43+
async with TemporaryUnixListener(partial(handler, client, method), path=path) as addr:
4244
yield addr

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

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,65 @@
11
from contextlib import AbstractContextManager
2+
from ipaddress import IPv6Address, ip_address
3+
from threading import Event
24

3-
from .adapters import DbusAdapter
5+
import asyncclick as click
6+
7+
from .adapters import DbusAdapter, TcpPortforwardAdapter, UnixPortforwardAdapter
48
from .driver import DbusNetwork
59
from jumpstarter.client import DriverClient
610

711

812
class NetworkClient(DriverClient):
9-
pass
13+
def cli(self):
14+
@click.group
15+
def base():
16+
"""Generic Network Connection"""
17+
pass
18+
19+
@base.command()
20+
@click.option("--address", default="localhost", show_default=True)
21+
@click.argument("port", type=int)
22+
def forward_tcp(address: str, port: int):
23+
"""
24+
Forward local TCP port to remote network
25+
26+
PORT is the TCP port to listen on.
27+
"""
28+
29+
with TcpPortforwardAdapter(
30+
client=self,
31+
local_host=address,
32+
local_port=port,
33+
) as addr:
34+
host = ip_address(addr[0])
35+
port = addr[1]
36+
match host:
37+
case IPv6Address():
38+
click.echo("[{}]:{}".format(host, port))
39+
case _:
40+
click.echo("{}:{}".format(host, port))
41+
42+
Event().wait()
43+
44+
@base.command()
45+
@click.argument("path", type=click.Path(), required=False)
46+
def forward_unix(path: str | None):
47+
"""
48+
Forward local Unix domain socket to remote network
49+
50+
PATH is the path of the Unix domain socket to listen on,
51+
defaults to a random path under $XDG_RUNTIME_DIR.
52+
"""
53+
54+
with UnixPortforwardAdapter(
55+
client=self,
56+
path=path,
57+
) as addr:
58+
click.echo(addr)
59+
60+
Event().wait()
61+
62+
return base
1063

1164

1265
class DbusNetworkClient(NetworkClient, AbstractContextManager):

packages/jumpstarter-driver-network/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies = [
1414
"pexpect>=4.9.0",
1515
"fabric>=3.2.2",
1616
"wsproto>=1.2.0",
17+
"asyncclick>=8.1.8",
1718
]
1819

1920
[project.entry-points."jumpstarter.drivers"]

packages/jumpstarter/jumpstarter/common/tempfile.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from contextlib import asynccontextmanager, contextmanager
1+
from contextlib import asynccontextmanager, contextmanager, nullcontext
2+
from os import PathLike
23
from pathlib import Path
34
from socket import AddressFamily
45
from tempfile import TemporaryDirectory
@@ -15,8 +16,13 @@ def TemporarySocket():
1516

1617

1718
@asynccontextmanager
18-
async def TemporaryUnixListener(handler):
19-
with TemporarySocket() as path:
19+
async def TemporaryUnixListener(handler, path: PathLike | None = None):
20+
if path is not None:
21+
cm = nullcontext(path)
22+
else:
23+
cm = TemporarySocket()
24+
25+
with cm as path:
2026
async with await create_unix_listener(path) as listener:
2127
async with create_task_group() as tg:
2228
tg.start_soon(listener.serve, handler, tg)

uv.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)