Container Control Core for Showrunner
Project description
Container Control
One immutable core + lightweight adapter + optional services = zero boilerplate.
This repository explains how to integrate any containerised workload with Showrunner using the Container Control Core (CCC) v1.1.
The core now provides optional built-in services for process management, metrics collection, traffic control, and privileged operations - reducing the complexity of your adapters even further.
Contents
- Concept
- Repository Layout
- Files You Care About
- Core Services
- Installation
- Quickstart (
container-control-bootstrap) - Building an Adapter
- Configuration (
config.yaml) - Dockerfile Template
- API Reference
- Operational Tips
- Publishing to PyPI
Concept
(root, FastAPI)
┌──────────────────────────────────────────┐
│ container_control_core.py (v1.1) │
│ • HTTP API (/api/*, /metrics) │
│ • lifecycle / state / signals │
│ • container-level metrics │
│ • privilege separation helper │
│ • OPTIONAL SERVICES: │
│ - Process Management │
│ - Enhanced Metrics Collection │
│ - Traffic Control (tc) │
│ - Privileged Commands │
└──────────────┬───────────────────────────┘
│ imports adapter class
▼
app_adapter.py ← common interface
│ subclassed by you
▼
my_adapter.py ← 5‑30 LOC typical (v1.1)
│ calls (if needed)
▼
Your real workload (async code, binary, …)
All containers share the exact same container_control_core.py v1.1; only my_adapter.py and config.yaml vary per application. The core's optional services can eliminate most boilerplate code.
Repository Layout
This project follows the standard src/ layout so the packaged wheel and the
checked-out repository have the same module structure:
src/
├── app_adapter.py
├── bootstrap.py
├── container_control/
│ ├── __init__.py
│ ├── scaffold.py
│ └── templates/
│ ├── Dockerfile.example
│ └── config.yaml.example
└── container_control_core.py
Working inside a clone, install dependencies with pip install -e . so the
src/ directory is on the Python path. The automated test suite mirrors this
layout by placing src/ at the front of sys.path.
Code map for automation & code-reading agents
Agents or humans inspecting the published wheel will see the same structure as the source tree above. The key modules and the responsibilities they cover are:
| Module | What it provides | Typical consumers |
|---|---|---|
container_control_core |
The FastAPI service. Hosts REST, Prometheus, optional process/metrics/traffic/privileged services. | Runtime containers only. The file ships verbatim so adapters can rely on a stable API. |
app_adapter |
Abstract base class adapters must subclass. Documents lifecycle hooks such as start, stop, and update. |
Adapter authors. Keep this file identical across services. |
bootstrap |
CLI entry point container-control-bootstrap. Calls write_scaffold() to copy the canonical files into a target directory. |
Developers who want to initialise a project or refresh the canonical files. |
container_control/__init__.py |
Convenience exports so from container_control import write_scaffold works without drilling into submodules. Also exposes the installed version. |
Library users and automation tools. |
container_control/scaffold.py |
The implementation behind scaffolding helpers. Loads byte-for-byte copies of the canonical modules and resource templates. | CLI + advanced users needing programmatic access. |
container_control/templates/ |
Template assets for config.yaml and the Dockerfile example. |
Scaffolding utilities. |
When browsing the wheel contents you can therefore start with
container_control_core to understand the runtime API and then follow imports
into app_adapter to see the adapter contract. The remaining modules focus on
setting up those two canonical files.
Files You Care About
In this repository the canonical source files live under src/, but the
scaffold command copies them into the destination project root.
| File | Keep unmodified? | Purpose |
|---|---|---|
container_control_core.py |
Yes | FastAPI service including the /api/update endpoint. |
app_adapter.py |
Yes | Abstract base class defining the contract. |
my_adapter.py |
No | Your shim – implements the contract. |
config.yaml |
No | Declares which adapter to load & options. |
Dockerfile |
No | Builds the image using the template below. |
Core Services
Container Control Core v1.1 includes optional built-in services that can handle common container operations, reducing the complexity of your adapters:
Process Management Service
- Purpose: Let the core manage your application process lifecycle
- Benefits: Automatic process monitoring, graceful shutdown, PID tracking
- Configuration:
process_management.enabled: true - Use case: When your workload is a simple command/binary
Enhanced Metrics Service
- Purpose: Automatic collection of network and process metrics
- Benefits: Zero-code monitoring, Prometheus-compatible output
- Configuration:
metrics.network_monitoring.enabled: true - Use case: When you need standard container metrics without custom code
Traffic Control Service
- Purpose: Built-in network shaping using Linux
tc - Benefits: Bandwidth limiting, latency simulation, no custom networking code
- Configuration:
traffic_control.enabled: true - Requirements:
CAP_NET_ADMINcapability,iproute2package
Privileged Commands Service
- Purpose: Declarative execution of privileged commands at lifecycle events
- Benefits: System tuning, firewall rules, network setup without custom hooks
- Configuration:
privileged_commands.pre_start: [...] - Use case: When you need root-level system configuration
Installation
Install the core and scaffolding tools directly from PyPI:
pip install container-control
This installs the runtime modules (container_control_core.py,
app_adapter.py, the optional FastAPI application) along with helper
templates and a Bootstrap CLI. You can also import
container_control.ApplicationAdapter or the scaffolding helpers from
Python code.
Quickstart (container-control-bootstrap)
Run the helper CLI to copy the core files into your project:
container-control-bootstrap /path/to/your/app
This creates container_control_core.py, app_adapter.py, config.yaml,
and a skeleton adapter. A Dockerfile is also copied if none exists.
Pass --adapter other_name.py to pick a different adapter filename. The
command only generates the config, Dockerfile, and adapter stub if those
files are missing so that you can re-run it safely to refresh the core
modules.
Programmatic scaffolding is available via:
from container_control import write_scaffold
write_scaffold("/path/to/your/app")
Building an Adapter
Copy app_adapter.py into your repo, then create my_adapter.py. With v1.1's core services, adapters can be much simpler:
Simple Adapter (using core services)
from app_adapter import ApplicationAdapter
class MyAdapter(ApplicationAdapter):
def start(self, payload, *, ensure_user):
# With process_management.enabled: true, this can be empty!
# The core handles the process based on config.yaml
return None # Core manages everything
def stop(self):
# With process_management.enabled: true, this can be empty!
pass
def get_metrics(self):
# With metrics services enabled, just return app-specific metrics
return {"custom_metric": self.get_some_value()}
Traditional Adapter (manual process management)
from __future__ import annotations
import subprocess
from app_adapter import ApplicationAdapter
class MyAdapter(ApplicationAdapter):
def start(self, payload, *, ensure_user):
cmd = ["python3", "my_tool.py", "--flows", payload["flowfile"]]
self.proc = subprocess.Popen(ensure_user(cmd))
return self.proc # opaque handle
def stop(self):
if self.proc and self.proc.poll() is None:
self.proc.terminate()
self.proc.wait(timeout=5)
def update(self, payload):
level = payload.get("log_level")
if level is None:
return False
subprocess.run(["kill", "-USR1", str(self.proc.pid)])
return True
def get_metrics(self):
return {"running": self.proc.poll() is None}
def pre_start_hooks(self, payload):
# example privileged setup
bw = payload.get("bandwidth", 20)
subprocess.run(
["tc", "qdisc", "add", "dev", "eth0", "root",
"tbf", "rate", f"{bw}mbit", "latency", "50ms", "burst", "32k"],
check=True,
)
v1.1 adapter size: Often < 15 lines when using core services! Traditional adapter size: 20-40 lines when managing everything manually.
Configuration (config.yaml)
Minimal Configuration
adapter:
class: my_adapter.MyAdapter # dotted-path import
primary_payload_key: flowfile # key that must exist in /api/start body
run_as_user: app_user # null ⇒ run as root
Full Configuration (with core services)
# --- Adapter Configuration (Required) ---
adapter:
class: my_adapter.MyAdapter
primary_payload_key: target_url
run_as_user: app_user
# --- Core Process Management (Optional) ---
process_management:
enabled: true
# Use command_factory for dynamic commands (recommended)
command_factory: "my_adapter.MyAdapter.build_command"
# OR use static template commands
# command: ["./my-tool", "--url", "{target_url}", "--threads", "{threads}"]
# --- Core Metrics Service (Optional) ---
metrics:
network_monitoring:
enabled: true
interface: "eth0"
process_monitoring:
enabled: true # Only works with process_management enabled
# --- Core Traffic Control (Optional) ---
traffic_control:
enabled: true
interface: "eth0"
bandwidth_mbps_key: "bandwidth_limit"
default_bandwidth_mbps: 50
latency_ms_key: "latency"
default_latency_ms: 20
# --- Privileged Commands (Optional) ---
privileged_commands:
pre_start:
- ["sysctl", "-w", "net.core.somaxconn=4096"]
- ["iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED", "-j", "ACCEPT"]
post_stop:
- ["iptables", "-F"]
If your workload never needs privilege-drop, omit run_as_user. If using core services, your adapter can be much simpler.
Dockerfile Template
FROM python:3.11-slim
ENV TZ=UTC PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
# Required for traffic control and privileged commands services
RUN apt-get update && apt-get install -y --no-install-recommends \
iproute2 iptables sudo && \
apt-get clean && rm -rf /var/lib/apt/lists/*
ARG APP_USER=app_user
RUN useradd -ms /bin/bash ${APP_USER} && \
echo "${APP_USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
pip install --no-cache-dir fastapi uvicorn psutil ruamel.yaml
# --- Core + interface + adapter + config
COPY container_control_core.py .
COPY app_adapter.py .
COPY my_adapter.py .
COPY config.yaml .
# --- Your actual application source/binaries
COPY . .
# Requires CAP_NET_ADMIN for traffic control service
EXPOSE 8080
CMD ["python", "-m", "uvicorn", "container_control_core:app", "--host", "0.0.0.0", "--port", "8080"]
API Reference
| Method | Path | Description |
|---|---|---|
POST |
/api/start |
Start the workload (or restart if running). Payload must include the key named in config.yaml. |
POST |
/api/update |
Live config tweak (e.g., traffic control) without restart. Returns 200 on success, 409 if unsupported, 400 if the app is not running. |
POST |
/api/stop |
Graceful stop. |
GET |
/api/metrics |
JSON with container + adapter metrics. |
GET |
/metrics |
Prometheus exposition. |
GET |
/api/health |
Simple liveness check. |
All timestamps are UTC ISO-8601 (YYYY-MM-DDTHH:MM:SS.mmmmmmZ).
Operational Tips
Container Deployment
- Kubernetes readiness probe →
/api/health - Horizontal scaling: watch
cpu_percentfrom/api/metrics - Security: Core services handle privileged operations; main workload runs as
app_user - Capabilities: Add
CAP_NET_ADMINif using traffic control service
Monitoring & Metrics
- Prometheus scraping →
/metricsendpoint - Core metrics: CPU, memory, network I/O automatically included
- Process metrics: Available when using
process_managementservice - Custom metrics: Return from your adapter's
get_metrics()method
Configuration Management
- Environment: Set
CCC_CONFIG_FILE=/path/to/config.yamlto override config location - Logging: Set
LOG_LEVEL=DEBUGfor detailed core service logs - Process management: Use
command_factoryfor dynamic command generation
Core Services Best Practices
- Process Management: Use for simple binaries/commands; traditional adapters for complex async code
- Traffic Control: Requires
CAP_NET_ADMINandiproute2package - Privileged Commands: Use for system tuning, firewall rules, network setup
- Metrics: Enable network/process monitoring for zero-code observability
Upgrading
- Core updates: Replace
container_control_core.pyin the image - Adapter compatibility: v1.1 is backward compatible with v1.x adapters
- New services: Add optional service configs without breaking existing deployments
Publishing to PyPI
Container Control is published through the manual Publish to PyPI
workflow (.github/workflows/publish.yml). The job uses PyPI's Trusted
Publishers integration with GitHub OpenID Connect, so no long-lived API
tokens are stored in the repository.
One-time setup
- In Settings → Environments, create an environment named
pypiand set the environment URL to https://pypi.org/project/container-control/. - On PyPI, add a Trusted Publisher with the following values:
- PyPI project name:
container-control - Owner:
rdwr-taly - Repository name:
container-control - Workflow name:
publish.yml - Environment name:
pypi
- PyPI project name:
- (Optional) Require reviewers or branch protections on the
pypienvironment before the workflow can deploy.
No repository secrets or variables are required; PyPI issues a short-lived token to the workflow at runtime via OIDC.
Releasing
- Update
CHANGELOG.mdand bump the version inpyproject.toml. - Run the test suite (
pytest) to confirm behaviour before publishing. - Trigger Publish to PyPI from the Actions tab and supply the version
from
pyproject.tomlwhen prompted. - The workflow builds fresh distributions with
python -m buildand, after the environment approval (if configured), uploads them to PyPI.
Happy Showrunning! :clapper:
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file container_control-1.1.0.tar.gz.
File metadata
- Download URL: container_control-1.1.0.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
103981077fb6aee49633e30ae96b6277155cccb58ea6c76ecc68ca8b2453f396
|
|
| MD5 |
fa75257041e7b42d1518660fd729e101
|
|
| BLAKE2b-256 |
56fa89f9ef087337254f43fb53e6da302bab7d31b4167fd96e96fbd1462d5d09
|
Provenance
The following attestation bundles were made for container_control-1.1.0.tar.gz:
Publisher:
publish.yml on rdwr-taly/container-control
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
container_control-1.1.0.tar.gz -
Subject digest:
103981077fb6aee49633e30ae96b6277155cccb58ea6c76ecc68ca8b2453f396 - Sigstore transparency entry: 524381057
- Sigstore integration time:
-
Permalink:
rdwr-taly/container-control@0c65d9e0f2bdcd37ed1048b03f365828962e0186 -
Branch / Tag:
refs/heads/v1.1.0 - Owner: https://github.com/rdwr-taly
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0c65d9e0f2bdcd37ed1048b03f365828962e0186 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file container_control-1.1.0-py3-none-any.whl.
File metadata
- Download URL: container_control-1.1.0-py3-none-any.whl
- Upload date:
- Size: 21.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c056ee9339f261be6e95978528dae879857b46cd1ede7b52ac263ce9f5ac7eb
|
|
| MD5 |
1ed50efc0e3c8fb67505834e809e1dc7
|
|
| BLAKE2b-256 |
ecc0fb48cb4a544e3ea3849eeac1afeb63dcb992c08ec218f59ada124efe2df6
|
Provenance
The following attestation bundles were made for container_control-1.1.0-py3-none-any.whl:
Publisher:
publish.yml on rdwr-taly/container-control
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
container_control-1.1.0-py3-none-any.whl -
Subject digest:
5c056ee9339f261be6e95978528dae879857b46cd1ede7b52ac263ce9f5ac7eb - Sigstore transparency entry: 524381082
- Sigstore integration time:
-
Permalink:
rdwr-taly/container-control@0c65d9e0f2bdcd37ed1048b03f365828962e0186 -
Branch / Tag:
refs/heads/v1.1.0 - Owner: https://github.com/rdwr-taly
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0c65d9e0f2bdcd37ed1048b03f365828962e0186 -
Trigger Event:
workflow_dispatch
-
Statement type: