Skip to content

Commit

Permalink
python: major peer initialization overhaul
Browse files Browse the repository at this point in the history
Our PeerStateListener and SuperuserStartup (and DBusStartup and
ControlMessageStartup) objects had a strange and overlapping role which
was basically two things:

 - notification of completion of a task, with possible notification of
   failure, including reporting of exceptions

 - an authentication hook

The first thing is basically something like a Future, which is
equivalent to an asynchronous function call.  In some places, we were
even bridging this gap, such as via DBusStartup creating a future for
the (async) D-Bus call to wait on.

Let's push things away from these custom types and towards a more
unified approach of asynchronous function calls for startup of peers.
Failures are reported via exceptions and completion is reported by
returning.

For the authentication hook usecase, we can use the existing
ferny.InteractionResponder interface.  It also works as an async
method call, so add some async helpers to CockpitServerProtocol for
sending/receiving authorize challenge/responses asynchronously.

Drop our own custom askpass code and use ferny's.  Also drop all
handling of authorize control messages from Peer — this all happens via
the ferny InteractionAgent now.  Our "pseudo" test helper needs a
rewrite to deal with that.

We also drop our send-stderr control message solution that we copied
from the C bridge and replace it with a solution which passes the
original stderr over the redirected stderr from the ferny agent.

Add a bunch of unit tests to check that peers behave properly in various
situations.
  • Loading branch information
allisonkarlitskaya authored and martinpitt committed Mar 17, 2023
1 parent ce8db46 commit 7d82d3c
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 369 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = '0'

[project.scripts]
cockpit-bridge = "cockpit.bridge:main"
cockpit-askpass = "cockpit.askpass:main"
cockpit-askpass = "cockpit._vendor.ferny.interaction_client:main"

[tool.setuptools.packages.find]
where = ["src"]
Expand Down
79 changes: 0 additions & 79 deletions src/cockpit/askpass.py

This file was deleted.

25 changes: 7 additions & 18 deletions src/cockpit/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from .peer import PeersRoutingRule
from .remote import HostRoutingRule
from .router import Router
from .superuser import SUPERUSER_AUTH_COOKIE, SuperuserRoutingRule
from .superuser import SuperuserRoutingRule
from .transports import StdioTransport

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -107,12 +107,6 @@ def do_init(self, message: Dict[str, object]) -> None:
if isinstance(superuser, dict):
self.superuser_rule.init(superuser)

def do_authorize(self, message: Dict[str, object]) -> None:
if message.get('cookie') == SUPERUSER_AUTH_COOKIE:
response = message.get('response')
if isinstance(response, str):
self.superuser_rule.answer(response)

def do_send_init(self) -> None:
self.write_control(command='init', version=1,
checksum=self.packages.checksum,
Expand Down Expand Up @@ -151,20 +145,15 @@ async def run(args) -> None:


def try_to_receive_stderr():
# We need to take great care to ensure that `stdin_socket` doesn't fall out
# of scope before we call .detach() on it — that would close the stdin fd.
stdin_socket = None
fds = []
try:
stdin_socket = socket.socket(fileno=0)
request = '\n{"command": "send-stderr"}\n'
stdin_socket.send(f'{len(request)}\n{request}'.encode('ascii'))
_msg, fds, _flags, _addr = socket.recv_fds(stdin_socket, 0, 1)
stderr_socket = socket.fromfd(2, socket.AF_UNIX, socket.SOCK_STREAM)
ours, theirs = socket.socketpair()
socket.send_fds(stderr_socket, [b'\0ferny\0(["send-stderr"], {})'], [theirs.fileno(), 1])
theirs.close()
_msg, fds, _flags, _addr = socket.recv_fds(ours, 1, 1)
except OSError:
return
finally:
if stdin_socket is not None:
stdin_socket.detach()
del stdin_socket

if fds:
# This is our new stderr. We have to be careful not to leak it.
Expand Down
Loading

0 comments on commit 7d82d3c

Please sign in to comment.