- Move app/, worker/, websocket_server/, migrations/, tests and the services to core/, which becomes a self-contained root - .gitmodules path for the novnc submodule rewritten by git mv. - Initial Step to split into core and cloud/community
External smoke test runner (playbook-driven)
Overview This tool simulates a customer calling the API and validates both control-plane and data-plane behavior:
- Creates containers (new pod or add to existing pod) and waits for scheduling and running status.
- Retrieves ingress info (DNS hostname and/or external IP:port) from the pod API and probes connectivity.
- Validates HTTP responses for expected content.
- Performs teardown and verifies deletion at the control plane.
- Includes a negative case for a bogus image that should fail to launch.
Key server routes the tool relies on:
- Create containers (async DB-first): /workloads/containers
- Container status updates (by worker): def update_container_workload()
- Pod introspection (with port_forwardings + DNS): def get_pods(), def get_pod()
- Pod update and deletion plumbing: def send_pod_deletion_update()
- Allocation flow and DNS/tunnel creation: def allocate_and_dispatch(), def allocate_ports_for_container(), class CloudflareTunnelManager
Project files
- Runner: smoke/runner.py
- Config and playbook model: smoke/config.py
- Sample playbook: smoke/playbook.example.yaml
- Package init: smoke/__init__.py
- API client uplift: IaaSClient.container_lifecycle()
Install
- Python 3.10+
- Dependencies:
- requests (already required by the API client)
- pyyaml (for YAML playbooks)
Install PyYAML: pip install pyyaml
Usage
- Set required environment variables or edit the playbook:
- API_BASE_URL (default http://172.17.0.1:5000/api)
- API_KEY
- VDC_ID (target Virtual Data Center UUID)
- WEBSOCKET_SERVER_URL (optional; informational)
- DNS_REQUIRED=true (the example playbook enforces DNS checks)
- Adjust smoke/playbook.example.yaml:
- api.base_url: the API endpoint (defaults match local dev)
- api.api_key: provide your API key
- api.vdc_id: paste a valid VDC UUID
- defaults.dns_required: keep true to enforce DNS resolution
- scenarios: three are provided by default:
- create-nginx (new pod with nginx)
- add-second-container (adds to the previous pod)
- negative-image (bogus image expected to fail)
-
Run the runner: python smoke/runner.py --playbook smoke/playbook.example.yaml --report smoke/out/report.json
-
Check the summary and report file:
- Console shows scenarios total/passed/failed.
- The JSON report contains:
- scenario-level timings (started_at, allocated_at, running_at, deleted_at)
- ingress endpoints discovered
- endpoint probe results (DNS, TCP, HTTP)
- final pass/fail
Scenarios implemented
- create-nginx
- Creates a new pod with one nginx container exposing internal:80 with external:0 (allocator chooses) and use_dns:true.
- Waits for allocated → running status.
- Fetches pod and probes:
- DNS resolve if dns_required
- TCP connect to external IP:port (or hostname)
- HTTP GET / expecting 200 with substring "nginx"
- Teardown: delete container and wait for deletion.
- add-second-container
- Adds a second container (default nginx) to the pod created in scenario 1.
- Waits for allocation and running.
- Probes the new container, and probes the first container still works.
- Teardown: deletes only the new container.
- negative-image
- Attempts to create a container using a bogus image (thisdoesnotexist.invalid:never).
- Expects final status launch_failed.
- Asserts no ingress is assigned.
- Teardown: deletes the failed container record.
Configuration model (Playbook)
- See smoke/config.py for the full schema and environment overrides.
- Playbook keys:
- api: base_url, api_key, websocket_url, vdc_id
- defaults: happy_image, alt_image, dns_required, default_domain, cleanup mode, timeouts
- execution: concurrency, stop_on_first_failure, tags include/exclude
- scenarios: list of test steps:
- type: create_container | add_container_to_pod | negative_image
- image, container_name_prefix, ports, use_existing_pod, expect.{final_status,http}
Notes and next steps
- The runner validates control-plane and data-plane behaviors. For deeper teardown assertions (e.g., confirm port_forwardings removed post-delete), add an extra step in runner to re-read the pod and assert the deleted container has no remaining port mappings and that TCP fails to connect. The scaffolding makes this straightforward.
- Container lifecycle validation (restart) can leverage def container_lifecycle_action() via IaaSClient.container_lifecycle() and then assert the worker-reported transition (e.g., pending-restart → running) plus a data-plane blip/resume as needed.
- To expand coverage: storage volumes, additional networks, VM workflows, pod lifecycle actions, and performance timings.
Example CI invocation
- Prepare a job with secrets for API_KEY and VDC_ID.
- Run: export API_KEY=; export VDC_ID=; python smoke/runner.py -p smoke/playbook.example.yaml -r smoke/out/report.json
- Upload smoke/out/report.json as an artifact.