185 lines
4.7 KiB
Python
185 lines
4.7 KiB
Python
'''
|
|
`TunnelledAddress` delegation + peeling semantics.
|
|
|
|
A tunnel annotates an existing L4 addr rather than being its own
|
|
transport, so the contract under test is mostly *delegation*: the
|
|
runtime must not be able to tell a tunnelled addr from its
|
|
overlay, and **nothing** about the tunnel may cross the wire.
|
|
|
|
See `ai/tpt-backends/03_wg_tunnel_bindspace.md` §3.1/§3.4.
|
|
|
|
'''
|
|
from __future__ import annotations
|
|
|
|
import msgspec
|
|
import pytest
|
|
|
|
from tractor.discovery import (
|
|
TunnelledAddress,
|
|
WGTunnelSpec,
|
|
strip_tunnels,
|
|
tunnels_of,
|
|
)
|
|
from tractor.discovery._addr import (
|
|
is_wrapped_addr,
|
|
wrap_address,
|
|
)
|
|
from tractor.ipc._tcp import TCPAddress
|
|
|
|
|
|
# a valid-looking std-base64 `wg(8)` pubkey (32B -> 44 chars)
|
|
_PUBKEY: str = 'g3x7z0AdV1rM6UQU22CC7IL3/ivn4DzrE7ikDhCZ/Dc='
|
|
|
|
|
|
@pytest.fixture
|
|
def overlay() -> TCPAddress:
|
|
return TCPAddress('10.0.11.1', 1616)
|
|
|
|
|
|
@pytest.fixture
|
|
def spec() -> WGTunnelSpec:
|
|
return WGTunnelSpec(
|
|
peer_pubkey=_PUBKEY,
|
|
bearer=('192.168.1.50', 51820),
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def tunnelled(
|
|
overlay: TCPAddress,
|
|
spec: WGTunnelSpec,
|
|
) -> TunnelledAddress:
|
|
return TunnelledAddress(overlay=overlay, tunnel=spec)
|
|
|
|
|
|
def test_proto_key_delegates(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
):
|
|
'''
|
|
A tunnel has no transport of its own, so every table lookup
|
|
must see the *overlay's* proto-key.
|
|
|
|
'''
|
|
assert tunnelled.proto_key == overlay.proto_key == 'tcp'
|
|
|
|
|
|
def test_unwrap_is_identical_to_overlay(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
):
|
|
'''
|
|
The whole point: nothing new crosses the wire, so a peer
|
|
never has to understand tunnels.
|
|
|
|
'''
|
|
assert tunnelled.unwrap() == overlay.unwrap()
|
|
|
|
# and it must survive msgpack as-is
|
|
enc: bytes = msgspec.msgpack.encode(tunnelled.unwrap())
|
|
assert msgspec.msgpack.decode(enc) == list(overlay.unwrap())
|
|
|
|
|
|
def test_unwrap_roundtrips_back_to_plain_overlay(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
):
|
|
'''
|
|
`wrap_address()` on a tunnelled addr's unwrapped form yields
|
|
the *plain* overlay type — the tunnel is simply absent, which
|
|
is correct: it was never on the wire.
|
|
|
|
'''
|
|
rewrapped = wrap_address(tunnelled.unwrap())
|
|
assert type(rewrapped) is TCPAddress
|
|
assert rewrapped == overlay
|
|
assert not isinstance(rewrapped, TunnelledAddress)
|
|
|
|
|
|
def test_bindspace_and_validity_delegate(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
):
|
|
assert tunnelled.bindspace == overlay.bindspace
|
|
assert tunnelled.is_valid == overlay.is_valid
|
|
|
|
|
|
def test_is_wrapped_addr_accepts_tunnelled(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
):
|
|
'''
|
|
`TunnelledAddress` is deliberately absent from
|
|
`_address_types`, so `is_wrapped_addr()` needs its own
|
|
clause.
|
|
|
|
'''
|
|
assert is_wrapped_addr(overlay)
|
|
assert is_wrapped_addr(tunnelled)
|
|
# the unwrapped form is NOT a wrapped addr
|
|
assert not is_wrapped_addr(tunnelled.unwrap())
|
|
|
|
|
|
def test_namespace_comes_from_the_tunnel(
|
|
overlay: TCPAddress,
|
|
):
|
|
'''
|
|
First real consumer of `Address.namespace`, spec'd in the
|
|
protocol since day one and implemented by no backend.
|
|
|
|
'''
|
|
# XXX, "no backend implements it" is literal — the member
|
|
# isn't even declared, so this is `AttributeError` not `None`.
|
|
# This assert is the guard: when a backend finally declares
|
|
# `.namespace`, it fails and the `getattr()` fallback in
|
|
# `TunnelledAddress.namespace` can go.
|
|
assert not hasattr(overlay, 'namespace')
|
|
|
|
no_ns = TunnelledAddress(
|
|
overlay=overlay,
|
|
tunnel=WGTunnelSpec(peer_pubkey=_PUBKEY),
|
|
)
|
|
assert no_ns.namespace is None
|
|
|
|
in_ns = TunnelledAddress(
|
|
overlay=overlay,
|
|
tunnel=WGTunnelSpec(peer_pubkey=_PUBKEY, netns='wg-test'),
|
|
)
|
|
assert in_ns.namespace == ('netns', 'wg-test')
|
|
|
|
|
|
def test_strip_tunnels(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
spec: WGTunnelSpec,
|
|
):
|
|
# idempotent on a plain addr
|
|
assert strip_tunnels(overlay) is overlay
|
|
# peels one
|
|
assert strip_tunnels(tunnelled) is overlay
|
|
# and collapses a nested stack in one call
|
|
nested = TunnelledAddress(overlay=tunnelled, tunnel=spec)
|
|
assert strip_tunnels(nested) is overlay
|
|
|
|
|
|
def test_tunnels_of_is_outermost_first(
|
|
tunnelled: TunnelledAddress,
|
|
overlay: TCPAddress,
|
|
):
|
|
assert tunnels_of(overlay) == ()
|
|
assert tunnels_of(tunnelled) == (tunnelled.tunnel,)
|
|
|
|
inner_spec = WGTunnelSpec(peer_pubkey=_PUBKEY, iface='wg1')
|
|
nested = TunnelledAddress(
|
|
overlay=tunnelled,
|
|
tunnel=inner_spec,
|
|
)
|
|
assert tunnels_of(nested) == (inner_spec, tunnelled.tunnel)
|
|
|
|
|
|
def test_frozen(
|
|
tunnelled: TunnelledAddress,
|
|
):
|
|
with pytest.raises(AttributeError):
|
|
tunnelled.overlay = TCPAddress('127.0.0.1', 1)
|