Compare commits
6 Commits
3a779fb5cb
...
a2e6c10118
| Author | SHA1 | Date |
|---|---|---|
|
|
a2e6c10118 | |
|
|
95360fcdf1 | |
|
|
e9cc5f4a7b | |
|
|
5f0334c6c3 | |
|
|
175712ae04 | |
|
|
2637e6bdc5 |
|
|
@ -84,6 +84,35 @@ def _wait_for_daemon_ready(
|
||||||
timeout=poll_interval,
|
timeout=poll_interval,
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
elif tpt_proto == 'tipc':
|
||||||
|
# TIPC — `reg_addr` is the proto-keyed
|
||||||
|
# `('tipc', stype, instance, scope)` per
|
||||||
|
# `tractor.ipc._tipc.TIPCAddress.unwrap()`.
|
||||||
|
#
|
||||||
|
# NOTE, connecting *by name* IS the readiness
|
||||||
|
# probe: until the daemon `.bind()`s (i.e.
|
||||||
|
# publishes) the name, the kernel answers
|
||||||
|
# `EHOSTUNREACH` immediately — no timeout wait.
|
||||||
|
from tractor.ipc._tipc import (
|
||||||
|
AF_TIPC,
|
||||||
|
TIPC_ADDR_NAME,
|
||||||
|
)
|
||||||
|
_, stype, instance, scope = reg_addr
|
||||||
|
sock = socket.socket(AF_TIPC, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
sock.settimeout(poll_interval)
|
||||||
|
sock.connect((
|
||||||
|
TIPC_ADDR_NAME,
|
||||||
|
stype,
|
||||||
|
instance,
|
||||||
|
0, # domain: 0 == "anywhere in scope"
|
||||||
|
scope,
|
||||||
|
))
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# UDS — `reg_addr` is a `(filedir, sockname)`
|
# UDS — `reg_addr` is a `(filedir, sockname)`
|
||||||
# tuple per `tractor.ipc._uds.UDSAddress.unwrap`.
|
# tuple per `tractor.ipc._uds.UDSAddress.unwrap`.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,10 @@ from tractor.discovery._multiaddr import (
|
||||||
_tpt_proto_to_maddr,
|
_tpt_proto_to_maddr,
|
||||||
_maddr_to_tpt_proto,
|
_maddr_to_tpt_proto,
|
||||||
)
|
)
|
||||||
from tractor.discovery._addr import wrap_address
|
from tractor.discovery._addr import (
|
||||||
|
wrap_address,
|
||||||
|
_address_types,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_tpt_proto_to_maddr_mapping():
|
def test_tpt_proto_to_maddr_mapping():
|
||||||
|
|
@ -30,7 +33,12 @@ def test_tpt_proto_to_maddr_mapping():
|
||||||
'''
|
'''
|
||||||
assert _tpt_proto_to_maddr['tcp'] == 'tcp'
|
assert _tpt_proto_to_maddr['tcp'] == 'tcp'
|
||||||
assert _tpt_proto_to_maddr['uds'] == 'unix'
|
assert _tpt_proto_to_maddr['uds'] == 'unix'
|
||||||
assert len(_tpt_proto_to_maddr) == 2
|
assert _tpt_proto_to_maddr['tipc'] == 'tipc'
|
||||||
|
|
||||||
|
# NOTE, drive the expected set off the registration table
|
||||||
|
# (per the "drive-the-set-from-the-`Literal`" pattern) so
|
||||||
|
# adding a backend can't fail this for the wrong reason.
|
||||||
|
assert set(_tpt_proto_to_maddr) == set(_address_types)
|
||||||
|
|
||||||
|
|
||||||
def test_mk_maddr_tcp_ipv4():
|
def test_mk_maddr_tcp_ipv4():
|
||||||
|
|
@ -153,9 +161,12 @@ def test_maddr_to_tpt_proto_mapping():
|
||||||
|
|
||||||
'''
|
'''
|
||||||
assert _maddr_to_tpt_proto == {
|
assert _maddr_to_tpt_proto == {
|
||||||
'tcp': 'tcp',
|
maddr_proto: proto_key
|
||||||
'unix': 'uds',
|
for proto_key, maddr_proto in _tpt_proto_to_maddr.items()
|
||||||
}
|
}
|
||||||
|
assert _maddr_to_tpt_proto['tcp'] == 'tcp'
|
||||||
|
assert _maddr_to_tpt_proto['unix'] == 'uds'
|
||||||
|
assert _maddr_to_tpt_proto['tipc'] == 'tipc'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_maddr_tcp_ipv4():
|
def test_parse_maddr_tcp_ipv4():
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,20 @@ def get_rando_addr(
|
||||||
assert addr.sockpath.resolve()
|
assert addr.sockpath.resolve()
|
||||||
testrun_reg_addr = addr.unwrap()
|
testrun_reg_addr = addr.unwrap()
|
||||||
|
|
||||||
|
# NOTE, `.get_random()` already derives the service
|
||||||
|
# *instance* from a `uuid4`+pid-salted seed, so both the
|
||||||
|
# within- and cross-proc isolation the other 2 protos
|
||||||
|
# hand-roll above comes for free.
|
||||||
|
#
|
||||||
|
# XXX matters MORE here than for tcp/uds: a TIPC name
|
||||||
|
# clash doesn't raise `EADDRINUSE`, it silently
|
||||||
|
# round-robins connects between both publishers.
|
||||||
|
case 'tipc':
|
||||||
|
from tractor.ipc._tipc import TIPCAddress
|
||||||
|
addr: TIPCAddress = addr_type.get_random()
|
||||||
|
assert addr.is_valid
|
||||||
|
testrun_reg_addr = addr.unwrap()
|
||||||
|
|
||||||
# XXX, as sanity it should never the same as the default for the
|
# XXX, as sanity it should never the same as the default for the
|
||||||
# host-singleton registry actor.
|
# host-singleton registry actor.
|
||||||
assert def_reg_addr != testrun_reg_addr
|
assert def_reg_addr != testrun_reg_addr
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,23 @@ if TYPE_CHECKING:
|
||||||
_tpt_proto_to_maddr: dict[str, str] = {
|
_tpt_proto_to_maddr: dict[str, str] = {
|
||||||
'tcp': 'tcp',
|
'tcp': 'tcp',
|
||||||
'uds': 'unix',
|
'uds': 'unix',
|
||||||
|
'tipc': 'tipc',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# XXX, there is NO `/tipc` in the multiaddr protocol table yet
|
||||||
|
# (upstream track: gh #483 + multiformats/py-multiaddr#107), and
|
||||||
|
# `Multiaddr()` rejects an unregistered proto name outright.
|
||||||
|
#
|
||||||
|
# So until that lands `tipc` maddrs stay **`str`**-only — which
|
||||||
|
# `MsgTransport.maddr`s `Multiaddr|str` return type already
|
||||||
|
# allows and `MsgpackUDSStream.maddr` already exercises — and
|
||||||
|
# `parse_maddr()` special-cases the prefix BEFORE handing
|
||||||
|
# anything to `Multiaddr()`.
|
||||||
|
#
|
||||||
|
# This is also why gh #443's "always return `Multiaddr`" item
|
||||||
|
# stays blocked.
|
||||||
|
_tipc_maddr_prefix: str = '/tipc/'
|
||||||
|
|
||||||
# reverse mapping: multiaddr protocol name -> tractor proto_key
|
# reverse mapping: multiaddr protocol name -> tractor proto_key
|
||||||
_maddr_to_tpt_proto: dict[str, str] = {
|
_maddr_to_tpt_proto: dict[str, str] = {
|
||||||
v: k for k, v in _tpt_proto_to_maddr.items()
|
v: k for k, v in _tpt_proto_to_maddr.items()
|
||||||
|
|
@ -49,7 +64,7 @@ _maddr_to_tpt_proto: dict[str, str] = {
|
||||||
|
|
||||||
def mk_maddr(
|
def mk_maddr(
|
||||||
addr: 'Address',
|
addr: 'Address',
|
||||||
) -> Multiaddr:
|
) -> Multiaddr|str:
|
||||||
'''
|
'''
|
||||||
Construct a `Multiaddr` from a tractor `Address` instance,
|
Construct a `Multiaddr` from a tractor `Address` instance,
|
||||||
dispatching on the `.proto_key` to build the correct
|
dispatching on the `.proto_key` to build the correct
|
||||||
|
|
@ -75,6 +90,18 @@ def mk_maddr(
|
||||||
f'/{net_proto}/{host}/{maddr_proto}/{port}'
|
f'/{net_proto}/{host}/{maddr_proto}/{port}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# NOTE, interim `str`-only grammar (see the
|
||||||
|
# `_tipc_maddr_prefix` note above),
|
||||||
|
#
|
||||||
|
# /tipc/<stype>/<instance>/<scope>
|
||||||
|
#
|
||||||
|
# mirroring how `uds` maps onto the spec-legal `/unix`.
|
||||||
|
case 'tipc':
|
||||||
|
_, stype, instance, scope = addr.unwrap()
|
||||||
|
return (
|
||||||
|
f'/{maddr_proto}/{stype}/{instance}/{scope}'
|
||||||
|
)
|
||||||
|
|
||||||
case 'uds':
|
case 'uds':
|
||||||
filedir, filename = addr.unwrap()
|
filedir, filename = addr.unwrap()
|
||||||
filepath = Path(filedir) / filename
|
filepath = Path(filedir) / filename
|
||||||
|
|
@ -100,6 +127,17 @@ def parse_maddr(
|
||||||
# lazy imports to avoid circular deps
|
# lazy imports to avoid circular deps
|
||||||
from tractor.ipc._tcp import TCPAddress
|
from tractor.ipc._tcp import TCPAddress
|
||||||
from tractor.ipc._uds import UDSAddress
|
from tractor.ipc._uds import UDSAddress
|
||||||
|
from tractor.ipc._tipc import TIPCAddress
|
||||||
|
|
||||||
|
# XXX MUST come before `Multiaddr()` which rejects the
|
||||||
|
# not-yet-registered `/tipc` proto name outright.
|
||||||
|
if maddr_str.startswith(_tipc_maddr_prefix):
|
||||||
|
_, _, stype, instance, scope = maddr_str.split('/')
|
||||||
|
return TIPCAddress(
|
||||||
|
_stype=int(stype),
|
||||||
|
_instance=int(instance),
|
||||||
|
_scope=int(scope),
|
||||||
|
)
|
||||||
|
|
||||||
maddr = Multiaddr(maddr_str)
|
maddr = Multiaddr(maddr_str)
|
||||||
proto_names: list[str] = [
|
proto_names: list[str] = [
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,23 @@ from tractor.ipc._uds import (
|
||||||
UDSAddress,
|
UDSAddress,
|
||||||
MsgpackUDSStream,
|
MsgpackUDSStream,
|
||||||
)
|
)
|
||||||
|
from tractor.ipc._tipc import (
|
||||||
|
AF_TIPC,
|
||||||
|
TIPCAddress,
|
||||||
|
MsgpackTIPCStream,
|
||||||
|
)
|
||||||
|
|
||||||
# if TYPE_CHECKING:
|
# if TYPE_CHECKING:
|
||||||
# from tractor._addr import Address
|
# from tractor._addr import Address
|
||||||
|
|
||||||
|
|
||||||
Address = TCPAddress|UDSAddress
|
Address = TCPAddress|UDSAddress|TIPCAddress
|
||||||
|
|
||||||
# manually updated list of all supported msg transport types
|
# manually updated list of all supported msg transport types
|
||||||
_msg_transports = [
|
_msg_transports = [
|
||||||
MsgpackTCPStream,
|
MsgpackTCPStream,
|
||||||
MsgpackUDSStream
|
MsgpackUDSStream,
|
||||||
|
MsgpackTIPCStream,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -59,15 +65,17 @@ _key_to_transport: dict[
|
||||||
] = {
|
] = {
|
||||||
('msgpack', 'tcp'): MsgpackTCPStream,
|
('msgpack', 'tcp'): MsgpackTCPStream,
|
||||||
('msgpack', 'uds'): MsgpackUDSStream,
|
('msgpack', 'uds'): MsgpackUDSStream,
|
||||||
|
('msgpack', 'tipc'): MsgpackTIPCStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
# convert an Address wrapper to its corresponding transport type
|
# convert an Address wrapper to its corresponding transport type
|
||||||
_addr_to_transport: dict[
|
_addr_to_transport: dict[
|
||||||
Type[TCPAddress|UDSAddress],
|
Type[TCPAddress|UDSAddress|TIPCAddress],
|
||||||
Type[MsgTransport]
|
Type[MsgTransport]
|
||||||
] = {
|
] = {
|
||||||
TCPAddress: MsgpackTCPStream,
|
TCPAddress: MsgpackTCPStream,
|
||||||
UDSAddress: MsgpackUDSStream,
|
UDSAddress: MsgpackUDSStream,
|
||||||
|
TIPCAddress: MsgpackTIPCStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -108,6 +116,12 @@ def transport_from_stream(
|
||||||
case socket.AF_UNIX:
|
case socket.AF_UNIX:
|
||||||
transport = 'uds'
|
transport = 'uds'
|
||||||
|
|
||||||
|
# NOTE, `AF_TIPC` is linux-only in CPython so we
|
||||||
|
# match the `._tipc` constant (which carries a uapi
|
||||||
|
# fallback) rather than `socket.AF_TIPC`.
|
||||||
|
case _ if sock.family == AF_TIPC:
|
||||||
|
transport = 'tipc'
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
f'Unsupported socket family: {sock.family}'
|
f'Unsupported socket family: {sock.family}'
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,31 @@ class Aid(
|
||||||
__repr__ = pretty_struct.Struct.__repr__
|
__repr__ = pretty_struct.Struct.__repr__
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE, mirrors `.discovery._addr.UnwrappedAddress` but is
|
||||||
|
# re-declared here to dodge the circular import
|
||||||
|
# (`._addr` -> `.ipc._tcp` -> `.msg`).
|
||||||
|
#
|
||||||
|
# XXX this is the **wire** shape, so widening it is a wire-format
|
||||||
|
# change; keep the two decls in sync.
|
||||||
|
# XXX VARIADIC on purpose! `msgspec` rejects a union holding
|
||||||
|
# more than one array-like type ("Type unions may not contain
|
||||||
|
# more than one array-like (list, set, tuple) type"), so the two
|
||||||
|
# concrete shapes,
|
||||||
|
#
|
||||||
|
# ('127.0.0.1', 1616) # tcp
|
||||||
|
# ('/run/user/1000/tractor', 'x.sock') # uds
|
||||||
|
# ('tipc', 1953628160, 1616, 2) # proto-keyed (tipc)
|
||||||
|
#
|
||||||
|
# can't be spelled as `tuple[str, str|int]|tuple[str, int, int,
|
||||||
|
# int]`. Widen to one homogeneous variadic tuple instead.
|
||||||
|
#
|
||||||
|
# ?TODO, the real fix is the `UnwrappedAddress` proto-key
|
||||||
|
# migration (see `.discovery._addr`) after which this becomes a
|
||||||
|
# tagged union keyed off elem 0 and full per-proto validation
|
||||||
|
# comes back.
|
||||||
|
UnwrappedAddress = tuple[str|int, ...]
|
||||||
|
|
||||||
|
|
||||||
class SpawnSpec(
|
class SpawnSpec(
|
||||||
pretty_struct.Struct,
|
pretty_struct.Struct,
|
||||||
tag=True,
|
tag=True,
|
||||||
|
|
@ -213,8 +238,8 @@ class SpawnSpec(
|
||||||
|
|
||||||
# TODO: not just sockaddr pairs?
|
# TODO: not just sockaddr pairs?
|
||||||
# -[ ] abstract into a `TransportAddr` type?
|
# -[ ] abstract into a `TransportAddr` type?
|
||||||
reg_addrs: list[tuple[str, str|int]]
|
reg_addrs: list[UnwrappedAddress]
|
||||||
bind_addrs: list[tuple[str, str|int]]|None
|
bind_addrs: list[UnwrappedAddress]|None
|
||||||
|
|
||||||
|
|
||||||
# TODO: caps based RPC support in the payload?
|
# TODO: caps based RPC support in the payload?
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ if TYPE_CHECKING:
|
||||||
TransportProtocolKey = Literal[
|
TransportProtocolKey = Literal[
|
||||||
'tcp',
|
'tcp',
|
||||||
'uds',
|
'uds',
|
||||||
|
'tipc',
|
||||||
]
|
]
|
||||||
_def_tpt_proto: TransportProtocolKey = 'tcp'
|
_def_tpt_proto: TransportProtocolKey = 'tcp'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue