jsoncomm: transparently handle huge messages via fds

The existing jsoncomm is a work of beautiy. For very big arguments
however the used `SOCK_SEQPACKET` hits the limitations of the
kernel network buffer size (see also [0]). This lead to various
workarounds in #824,#1331,#1836 where parts of the request are
encoded as part of the json method call and parts are done via
a side-channel via fd-passing.

This commit changes the code so that the fd channel is automatically
and transparently created and the workarounds are removed. A test
is added that ensures that very big messages can be passed.

[0] https://github.com/osbuild/osbuild/pull/1833
This commit is contained in:
Michael Vogt 2024-08-07 16:49:30 +02:00 committed by Achilleas Koutsou
parent d67fa48c17
commit 0abdfb9041
6 changed files with 130 additions and 87 deletions

View file

@ -1,9 +1,7 @@
import json
import os
from unittest.mock import patch
from unittest.mock import call, patch
from osbuild import inputs
from osbuild.util.jsoncomm import FdSet
class FakeInputService(inputs.InputService):
@ -11,7 +9,7 @@ class FakeInputService(inputs.InputService):
# do not call "super().__init__()" here to make it testable
self.map_calls = []
def map(self, _store, origin, refs, target, options):
def map(self, store, origin, refs, target, options):
self.map_calls.append([origin, refs, target, options])
return "complex", 2, "reply"
@ -20,8 +18,6 @@ def test_inputs_dispatches_map(tmp_path):
store_api_path = tmp_path / "api-store"
store_api_path.write_text("")
args_path = tmp_path / "args"
reply_path = tmp_path / "reply"
args = {
"api": {
"store": os.fspath(store_api_path),
@ -31,17 +27,14 @@ def test_inputs_dispatches_map(tmp_path):
"target": "some-target",
"options": "some-options",
}
args_path.write_text(json.dumps(args))
reply_path.write_text("")
with args_path.open() as f_args, reply_path.open("w") as f_reply:
fd_args, fd_reply = os.dup(f_args.fileno()), os.dup(f_reply.fileno())
fds = FdSet.from_list([fd_args, fd_reply])
fake_service = FakeInputService(args="some")
with patch.object(inputs, "StoreClient"):
r = fake_service.dispatch("map", None, fds)
assert r == ('{}', None)
assert fake_service.map_calls == [
["some-origin", "some-refs", "some-target", "some-options"],
]
assert reply_path.read_text() == '["complex", 2, "reply"]'
fake_service = FakeInputService(args="some")
with patch.object(inputs, "StoreClient") as mocked_store_client_klass:
r = fake_service.dispatch("map", args, None)
assert mocked_store_client_klass.call_args_list == [
call(connect_to=os.fspath(store_api_path)),
]
assert fake_service.map_calls == [
["some-origin", "some-refs", "some-target", "some-options"],
]
assert r == (("complex", 2, "reply"), None)