feat(deploy): left4me-apply-cake helper with apply/clear modes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-10 00:52:16 +02:00
parent d783449d05
commit 878639147a
No known key found for this signature in database
2 changed files with 80 additions and 0 deletions

View file

@ -0,0 +1,47 @@
#!/bin/sh
# left4me — apply or clear CAKE egress shaper on the configured uplink.
# Driven by left4me-cake.service. See spec
# docs/superpowers/specs/2026-05-10-l4d2-network-shaping-design.md.
set -eu
mode=${1:-apply}
if [ -r /etc/left4me/cake.env ]; then
. /etc/left4me/cake.env
fi
resolve_iface() {
if [ -n "${LEFT4ME_UPLINK_IFACE:-}" ]; then
printf '%s' "$LEFT4ME_UPLINK_IFACE"
return
fi
ip -4 route show default | awk '/default/ {print $5; exit}'
}
case "$mode" in
apply)
if [ -z "${LEFT4ME_UPLINK_MBIT:-}" ]; then
echo "left4me-cake: LEFT4ME_UPLINK_MBIT unset; skipping shaper" >&2
exit 0
fi
iface=$(resolve_iface)
if [ -z "$iface" ]; then
echo "left4me-cake: cannot determine egress iface; skipping" >&2
exit 0
fi
exec tc qdisc replace dev "$iface" root cake \
bandwidth "${LEFT4ME_UPLINK_MBIT}mbit" \
internet diffserv4 dual-dsthost
;;
clear)
iface=$(resolve_iface)
if [ -z "$iface" ]; then
exit 0
fi
tc qdisc del dev "$iface" root 2>/dev/null || true
;;
*)
echo "usage: $0 [apply|clear]" >&2
exit 2
;;
esac

View file

@ -20,6 +20,7 @@ SYSTEMCTL_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-systemctl"
JOURNALCTL_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-journalctl" JOURNALCTL_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-journalctl"
OVERLAY_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-overlay" OVERLAY_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-overlay"
SCRIPT_SANDBOX_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-script-sandbox" SCRIPT_SANDBOX_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-script-sandbox"
APPLY_CAKE_HELPER = DEPLOY / "files/usr/local/libexec/left4me/left4me-apply-cake"
SANDBOX_RESOLV_CONF = DEPLOY / "files/etc/left4me/sandbox-resolv.conf" SANDBOX_RESOLV_CONF = DEPLOY / "files/etc/left4me/sandbox-resolv.conf"
CAKE_ENV = DEPLOY / "files/etc/left4me/cake.env" CAKE_ENV = DEPLOY / "files/etc/left4me/cake.env"
SUDOERS = DEPLOY / "files/etc/sudoers.d/left4me" SUDOERS = DEPLOY / "files/etc/sudoers.d/left4me"
@ -770,3 +771,35 @@ def test_cake_env_template_documents_required_knobs():
# Empty defaults: shaper unit no-ops with a journal warning when unset. # Empty defaults: shaper unit no-ops with a journal warning when unset.
assert "LEFT4ME_UPLINK_MBIT=" in text assert "LEFT4ME_UPLINK_MBIT=" in text
assert "LEFT4ME_UPLINK_IFACE=" in text assert "LEFT4ME_UPLINK_IFACE=" in text
def test_apply_cake_helper_supports_apply_and_clear_modes():
assert APPLY_CAKE_HELPER.is_file()
text = APPLY_CAKE_HELPER.read_text()
assert text.startswith("#!/bin/sh")
# Both knobs are read from the env file.
assert "LEFT4ME_UPLINK_MBIT" in text
assert "LEFT4ME_UPLINK_IFACE" in text
assert ". /etc/left4me/cake.env" in text
# Iface fallback to default route.
assert "ip -4 route show default" in text
# Two modes; default to apply.
assert "mode=${1:-apply}" in text
assert 'apply)' in text and 'clear)' in text
# Apply: idempotent `tc qdisc replace` with the documented flags.
assert "tc qdisc replace" in text
assert "cake" in text
assert "bandwidth" in text
assert "internet" in text
assert "diffserv4" in text
assert "dual-dsthost" in text
# Clear: tolerates a missing qdisc.
assert "tc qdisc del" in text
assert "|| true" in text
# Fail-soft on missing config.
assert "LEFT4ME_UPLINK_MBIT unset" in text
def test_apply_cake_helper_passes_shell_syntax_check():
subprocess.run(["sh", "-n", str(APPLY_CAKE_HELPER)], check=True)