Commit Graph

6 Commits

Author SHA1 Message Date
6392131577 style: satisfy the widened ruff default rule set
ruff 0.16 enables rules that earlier releases did not, so `ruff check .`
was already failing on HEAD with 35 findings - the CI job installs ruff
unpinned and would have gone red on the next push regardless of this
branch.

All changes are mechanical and behaviour-neutral:

- Optional[X] -> X | None, List[str] -> list[str], dropped the now unused
  typing imports (every module already carries
  `from __future__ import annotations`, and requires-python is >= 3.10).
- Sorted import blocks, `import automtu.x as x` -> `from automtu import x`.
- subprocess.run in pmtu.py and wg.py now passes check=False explicitly.
  That is the parameter default, so the calls behave exactly as before.
- int(round(x)) -> round(x): round() with a single argument already
  returns int.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 18:42:55 +02:00
499e61346d fix(net): detect interfaces without following /sys symlinks
/sys/class/net/<if> is always a symlink into /sys/devices; in nested
containers (sysbox runtime) that target is not visible, so the link is
dead. iface_exists() used Path.exists(), which follows the link and
therefore tested the visibility of the target instead of the existence of
the interface. detect_egress_iface() discarded every interface it had
correctly read from the routing table, so automtu aborted with "Could not
detect egress interface" and rc=2 even though `ip -4 route show default`
and `ip link show dev eth0` both worked. The documented escape hatch
--egress-if was equally dead, because core.py validates it through the
same call.

Chosen fix: os.path.lexists() rather than probing netlink for existence.
It asks the right question -- "is there an entry named <if>" -- and costs
no subprocess on a healthy host. Netlink (`ip link show`) is only the
fallback for when /sys/class/net itself is unavailable, so sysfs stays the
preferred path everywhere.

read_iface_mtu() gains the same cascade: sysfs first, MTU parsed from
`ip link show dev <if>` when the sysfs path is unreadable, RuntimeError
naming both sources when neither answers - no silent default. core.py
turns that into an error line plus rc=3 instead of a traceback.

list_ifaces() had the same defect (is_dir() on a dead symlink) and would
have left Docker bridge detection blind in the same environments.

Regression tests build a /sys replacement whose class/net/eth0 points at a
missing target and mock the ip command: they fail if lexists becomes
exists again, if the MTU fallback is removed, or if the symlink filter is
dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 18:35:25 +02:00
fc5dee4dc0 feat: add Docker MTU apply support and docker-ordered persistence
https://chatgpt.com/share/69733e45-96ec-800f-9aad-0cac7306dedd
2026-01-23 10:53:29 +01:00
f534d025d0 feat(persist): add systemd persistence backend with uninstall
https://chatgpt.com/share/69733e45-96ec-800f-9aad-0cac7306dedd
2026-01-23 10:23:59 +01:00
2bf7117296 feat(output): add machine-readable MTU output modes (single number + JSON)
https://chatgpt.com/share/697112b2-0410-800f-93ff-9372b603d43f
2026-01-21 19:06:47 +01:00
dcc7a68973 refactor: convert script to automtu package with CI workflow
https://chatgpt.com/share/697112b2-0410-800f-93ff-9372b603d43f
2026-01-21 18:53:44 +01:00