feat: add IaC layer with shared var/t1 and var/t3 data paths

Ansible provisions the k3d cluster and Flux controllers; Terraform
modules deploy the simulated fleet and ground warehouse. Compose and
k3d share var/t1 (live lake) and var/t3 (warehouse). The prototype
gains a live mode fed by the explorer read-only SQL API.
This commit is contained in:
2026-07-08 20:17:32 +01:00
parent d58569b25c
commit cb4664f5ee
44 changed files with 8067 additions and 35 deletions
+6 -4
View File
@@ -14,14 +14,16 @@ FLIGHT_ID=$(date -u +%Y%m%dT%H%MZ)-sim docker compose up --build --scale drone=5
DRONE_COUNT=10 DURATION_S=300 SPEEDUP=4 docker compose up --build --scale drone=10
```
Output lands in `./data/` with the standard layout:
Output lands in [`../var/t1/`](../var/t1/) (the shared T1 lake — same path the k3d fleet and warehouse offload use). Override with `SWARM_T1_DIR` if needed:
```
data/dataset=telemetry/flight=…/drone=…/sensor=imu/year=…/…/hour=…/data.parquet
data/dataset=detections/flight=…/drone=…/…
data/dataset=state/flight=…/drone=…/… ← sent + received broadcasts
var/t1/dataset=telemetry/flight=…/drone=…/sensor=imu/year=…/…/hour=…/data.parquet
var/t1/dataset=detections/flight=…/drone=…/…
var/t1/dataset=state/flight=…/drone=…/… ← sent + received broadcasts
```
Historical flights after offload live under [`../var/t3/`](../var/t3/) (T3 warehouse).
## Run a single drone without Docker
```bash
+3 -3
View File
@@ -14,7 +14,7 @@ services:
# Compose default bridge network: subnet-directed broadcast works
BROADCAST_ADDR: ${BROADCAST_ADDR:-255.255.255.255}
volumes:
- ./data:/data
- ${SWARM_T1_DIR:-../var/t1}:/data
# --- Monitoring profile: docker compose --profile monitoring up ---
@@ -26,7 +26,7 @@ services:
DATA_DIR: /data
SCAN_INTERVAL_S: "5"
volumes:
- ./data:/data:ro
- ${SWARM_T1_DIR:-../var/t1}:/data:ro
- ./monitoring:/app/monitoring:ro
ports:
- "${EXPORTER_PORT:-9105}:9105"
@@ -38,7 +38,7 @@ services:
environment:
DATA_DIR: /data
volumes:
- ./data:/data:ro
- ${SWARM_T1_DIR:-../var/t1}:/data:ro
- ./explorer:/app/explorer:ro
ports:
- "${EXPLORER_UI_PORT:-8088}:8088"
+10
View File
@@ -116,9 +116,19 @@ class Handler(BaseHTTPRequestHandler):
self.send_response(code)
self.send_header("Content-Type", ctype)
self.send_header("Content-Length", str(len(body)))
# Dev CORS: lets the prototype's live mode poll the API from another
# origin. Everything behind this is read-only by construction.
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(body)
def do_OPTIONS(self) -> None: # noqa: N802 — http.server API
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
def _json(self, payload: dict, code: int = 200) -> None:
self._send(code, json.dumps(payload, default=str).encode(), "application/json")