Pulls the 5 privileged helpers out of deploy/files/usr/local/{libexec,sbin}/
into top-level scripts/{libexec,sbin}/. They are application-inherent code
(invoked at runtime via sudo from l4d2host/l4d2web), not deploy artifacts —
the previous nesting under deploy/files/ confused source-of-truth with
install-target FHS layout.
deploy/ now means "reference exemplar": README explaining the target
layout, plus example sudoers / sysctl / sandbox-resolv.conf / env
templates / curated systemd units (the ones ckn-bw's reactor emits).
Anyone building a fresh deployment (other than ckn-bw) reads this tree.
Dead static artifacts deleted: left4me-apply-cake helper, left4me-cake
+ left4me-nft-mark service units, cake.env, left4me-mark.nft, and the
superseded deploy-test-server.sh installer.
Tests split to match the new shape:
- scripts/tests/{test_overlay,test_script_sandbox,test_systemctl_helper,
test_journalctl_helper,test_helpers_use_fixed_paths,test_sudoers_grants}.py
with shared fixtures in conftest.py
- deploy/tests/test_example_units.py (renamed from test_deploy_artifacts.py)
— slimmed to lock down the curated example units, sysctl, env templates
l4d2host/tests/test_overlay_helper.py: helper-source path updated to
scripts/libexec/left4me-overlay (was building the path segment-by-segment
under deploy/files/, missed by the path-prefix grep during pre-flight).
Runtime install-target paths (/usr/local/{libexec,sbin}/) unchanged, so
l4d2host/service_control.py, l4d2web/services/overlay_builders.py, the
sudoers grants, and the systemd units all keep their existing path
references.
Requires the matching ckn-bw change to bundles/left4me/items.py
(install_left4me_scripts repointed from /opt/left4me/src/deploy/files/...
to /opt/left4me/src/scripts/...). Left4me lands first so a fresh
git_deploy exposes the new source path before the bundle apply runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| templates | ||
| tests | ||
| __init__.py | ||
| cli.py | ||
| instances.py | ||
| logging.py | ||
| logs.py | ||
| paths.py | ||
| process.py | ||
| pyproject.toml | ||
| README.md | ||
| service_control.py | ||
| spec.py | ||
| status.py | ||
| steam_install.py | ||
l4d2-host-lib
Python host library and CLI for managing L4D2 instances.
CLI
l4d2ctl exposes these write commands in v1:
installinitialize <name> -f <spec.yaml>start <name>stop <name>delete <name>
It also exposes read commands used by the web app host boundary:
status <name> --jsonlogs <name> --lines <n> --follow/--no-follow
Subprocess failures are fail-fast. Raw stderr is written to stderr and the command exits with the same subprocess return code.
Runtime Paths
The host library reads LEFT4ME_ROOT from the environment. It defaults to /var/lib/left4me:
${LEFT4ME_ROOT}/installation${LEFT4ME_ROOT}/overlays/<overlay-ref>${LEFT4ME_ROOT}/instances/<name>${LEFT4ME_ROOT}/runtime/<name>/{upper,work,merged}${LEFT4ME_ROOT}/tmp
Overlay specs use relative refs below ${LEFT4ME_ROOT}/overlays, for example standard, competitive/base, or users/42/custom. Absolute refs, .., empty path components, and symlink escapes outside the overlays root are rejected.
systemd Integration
l4d2ctl start, stop, status, and logs use non-interactive sudo helper commands:
sudo -n /usr/local/libexec/left4me/left4me-systemctl ...sudo -n /usr/local/libexec/left4me/left4me-journalctl ...
Deployment/config management owns the global left4me-server@.service unit under /usr/local/lib/systemd/system. The host library does not install or manage the unit file directly.
Host Prerequisites
The host library intentionally does not install or preflight runtime dependencies. The target host must provide them before running l4d2ctl.
Validated on Debian 13 during the ckn@10.0.4.128 smoke test:
- Python 3.12+ with virtualenv/pip tooling for installing
l4d2host. steamcmdavailable onPATHand able to self-update as the runtime user.- 32-bit compatibility libraries for SteamCMD on amd64 Debian:
libc6-i386,lib32gcc-s1,lib32stdc++6. - Kernel overlayfs (
mount -t overlay); mount/umount go through theleft4me-overlayprivileged helper, whichnsenters into PID 1's mount namespace. systemctl --userandjournalctl --useravailable for the runtime user.- User lingering enabled when services must survive SSH sessions:
sudo loginctl enable-linger <user>. /var/lib/left4mecreated and writable by the runtime user, unlessLEFT4ME_ROOTis set to another deployment-managed root.
Example Debian setup:
sudo apt-get update
sudo apt-get install -y \
python3 python3-venv python3-pip \
curl ca-certificates tar gzip \
util-linux \
libc6-i386 lib32gcc-s1 lib32stdc++6
sudo mkdir -p /opt/steamcmd /var/lib/left4me/{installation,overlays,instances,runtime,tmp}
sudo chown -R "$USER:$USER" /opt/steamcmd /var/lib/left4me
sudo loginctl enable-linger "$USER"
SteamCMD should be installed so the runtime user can update it. If installing from Valve's tarball, avoid symlinking steamcmd.sh directly because it derives its install root from $0. Use a wrapper instead:
curl -fsSL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz -o /tmp/steamcmd_linux.tar.gz
tar -xzf /tmp/steamcmd_linux.tar.gz -C /opt/steamcmd
sudo tee /usr/local/bin/steamcmd >/dev/null <<'EOF'
#!/bin/sh
exec /opt/steamcmd/steamcmd.sh "$@"
EOF
sudo chmod 755 /usr/local/bin/steamcmd
chmod 755 /opt/steamcmd/steamcmd.sh /opt/steamcmd/linux32/steamcmd
steamcmd +quit
uv is optional deployment tooling. Debian 13 did not provide an uv package during the smoke test, so install it explicitly if you want to use it for faster virtualenv/dependency setup. l4d2ctl does not require uv at runtime.
Host-Local Read APIs
These Python read APIs back the CLI read commands and remain available for host-local callers:
get_instance_status(name)stream_instance_logs(name, lines=200, follow=True)