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

bridge: Report PID for a spawn stream channel #21132

Merged
merged 1 commit into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,9 @@ following options can be specified:
size of the terminal window. Values must be integers between 0 and 0xffff.
This option is only valid if "pty" is true.

For type "spawn", the `ready` message contains a "pid" field with the spawned
process ID.

If an "done" is sent to the bridge on this channel, then the socket and/or pipe
input is shutdown. The channel will send an "done" when the output of the socket
or pipe is done.
Expand Down
6 changes: 5 additions & 1 deletion src/cockpit/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ class ProtocolChannel(Channel, asyncio.Protocol):
_send_pongs: bool = True
_last_ping: 'JsonObject | None' = None
_create_transport_task: 'asyncio.Task[asyncio.Transport] | None' = None
_ready_info: 'JsonObject | None' = None

# read-side EOF handling
_close_on_eof: bool = False
Expand Down Expand Up @@ -400,7 +401,10 @@ def create_transport_done(self, task: 'asyncio.Task[asyncio.Transport]') -> None
return

self.connection_made(transport)
self.ready()
if self._ready_info is not None:
self.ready(**self._ready_info)
else:
self.ready()

def connection_made(self, transport: asyncio.BaseTransport) -> None:
assert isinstance(transport, asyncio.Transport)
Expand Down
4 changes: 3 additions & 1 deletion src/cockpit/channels/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ async def create_transport(self, loop: asyncio.AbstractEventLoop, options: JsonO

try:
transport = SubprocessTransport(loop, self, args, pty=pty, window=window, env=env, cwd=cwd, stderr=stderr)
logger.debug('Spawned process args=%s pid=%i', args, transport.get_pid())
pid = transport.get_pid()
self._ready_info = {'pid': pid}
logger.debug('Spawned process args=%s pid=%i', args, pid)
return transport
except FileNotFoundError as error:
raise ChannelError('not-found') from error
Expand Down
3 changes: 3 additions & 0 deletions test/pytest/test_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,9 @@ async def serve_page(reader, writer):
assert control['channel'] == ch
command = control['command']
if command == 'ready':
if 'spawn' in args:
assert isinstance(control['pid'], int)
assert os.readlink(f"/proc/{control['pid']}/exe").endswith("/cat")
# If we get ready, it's our turn to send data first.
# Hopefully we didn't receive any before.
assert not saw_data
Expand Down