install
install
¶
Install + uninstall the OCI hook that spawns the supervisor.
Single-root layout: scripts, ballast, and the JSON descriptor all
live under state_root() (which
honours the operator's paths.root config). containers.conf
is patched to list state_root() / "hooks" in hooks_dir so
podman scans the canonical terok-owned directory rather than the
default ~/.config/containers/oci/hooks.d/.
Files written:
<state_root>/hooks/supervisor_hook.py<state_root>/hooks/_supervisor_state.py<state_root>/hooks/terok-sandbox-supervisor-createRuntime.json<state_root>/hooks/terok-sandbox-supervisor-poststop.json(one OCI hook descriptor per stage — podman/crun reuse the samehook.argsfor every stage listed in a single descriptor, so each stage gets its own JSON; both match on theterok.sandbox.sidecarannotation)<state_root>/supervisor_wrapper.py(templated — embeds the resolvedterok-sandboxargv)
The supervisor flow is annotation-driven from here on: the launch
path emits --annotation terok.sandbox.sidecar=<abspath> and the
hook reads the sidecar at that path. No $XDG_* discovery,
no stamp files, no parallel root resolution.
install_supervisor_hooks(*, hooks_dir=None)
¶
Lay down hook scripts, wrapper, and the OCI descriptor.
hooks_dir — override for tests; defaults to
state_root() / "hooks", where the role scripts already live.
Scripts + ballast + descriptor share one terok-owned directory so
a teardown is a clean rm -rf. containers.conf is patched
to register that path.
Idempotent — every file write overwrites silently, and the
descriptor JSON gets re-rendered each time so a moved install
location is picked up on the next terok-sandbox setup.
Source code in src/terok_sandbox/supervisor/install.py
uninstall_supervisor_hooks(*, hooks_dir=None)
¶
Remove every file install_supervisor_hooks writes.
Idempotent — missing files are tolerated. Does not touch
per-container state (sidecar/, logs/, pids/ under the
state root) — those are sweep-able with a separate operator
command if needed.
Source code in src/terok_sandbox/supervisor/install.py
kill_all_supervisors()
¶
SIGKILL every live host-side supervisor process group, then sweep strays.
The OCI hook spawns each wrapper with start_new_session=True,
so the PID it records is also the process-group ID of that
container's entire supervisor tree — restart-loop wrapper,
supervisor, service children, watcher subprocesses. One
killpg per <state_root>/pids/supervisor-*.pid therefore
takes the whole tree down atomically, and it still works after the
wrapper itself died: a process group persists while any member
lives, and its ID cannot be recycled meanwhile. A second pass
(_kill_all_service_children) sweeps supervise-child
processes that no PID file reaches any more — an earlier reap
unlinked the file while the kill half failed. Each returned row
is (container_id, error_or_None) — None means the
processes are no longer there, whether we killed them or they had
already exited.
Designed for the panic path: the OCI poststop reap does a
graceful SIGTERM → poll → SIGKILL dance for a normal
container stop; panic skips straight to SIGKILL because the
whole point is to deny the supervisor any more cycles to answer
socket calls from a misbehaving container.