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
+273
View File
@@ -0,0 +1,273 @@
# Ground warehouse segment (T3, docs/03 + docs/06) — the stationary,
# long-lifecycle half of the system and therefore the most natural Terraform
# territory. Runs in the same k3d cluster as the fleet, in its own namespace
# and with its own state.
locals {
labels = { "app.kubernetes.io/part-of" = "swarm-ground" }
}
resource "kubernetes_namespace" "ground" {
metadata {
name = var.namespace
labels = local.labels
}
}
# --- MinIO object store -------------------------------------------------------
resource "kubernetes_secret" "minio" {
metadata {
name = "minio-credentials"
namespace = kubernetes_namespace.ground.metadata[0].name
}
data = {
MINIO_ROOT_USER = var.minio_root_user
MINIO_ROOT_PASSWORD = var.minio_root_password
}
}
resource "kubernetes_persistent_volume_claim" "minio" {
metadata {
name = "minio-data"
namespace = kubernetes_namespace.ground.metadata[0].name
}
spec {
access_modes = ["ReadWriteOnce"]
resources {
requests = { storage = var.minio_storage }
}
}
}
resource "kubernetes_deployment" "minio" {
metadata {
name = "minio"
namespace = kubernetes_namespace.ground.metadata[0].name
labels = local.labels
}
spec {
replicas = 1
strategy {
type = "Recreate" # RWO volume
}
selector {
match_labels = { app = "minio" }
}
template {
metadata {
labels = merge(local.labels, { app = "minio" })
}
spec {
container {
name = "minio"
image = "minio/minio:RELEASE.2024-06-13T22-53-53Z"
args = ["server", "/data", "--console-address", ":9001"]
env_from {
secret_ref {
name = kubernetes_secret.minio.metadata[0].name
}
}
# Air-gap hygiene: no update checks
env {
name = "MINIO_UPDATE"
value = "off"
}
port {
container_port = 9000
}
port {
container_port = 9001
}
volume_mount {
name = "store"
mount_path = "/data"
}
}
volume {
name = "store"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.minio.metadata[0].name
}
}
}
}
}
}
resource "kubernetes_service" "minio" {
metadata {
name = "minio"
namespace = kubernetes_namespace.ground.metadata[0].name
}
spec {
type = "NodePort"
selector = { app = "minio" }
port {
name = "api"
port = 9000
target_port = 9000
}
port {
name = "console"
port = 9001
target_port = 9001
node_port = var.node_ports.minio_console
}
}
}
# --- Post-flight offload: T1 lake -> T3 warehouse ------------------------------
# In production this runs on the docking station after a drone lands: only
# sealed partitions move, checksums are verified, then the on-board lake is
# pruned. The simulation compresses that into a periodic rsync-style copy
# plus an object-store mirror.
resource "kubernetes_cron_job_v1" "offload" {
metadata {
name = "offload"
namespace = kubernetes_namespace.ground.metadata[0].name
labels = local.labels
}
spec {
schedule = var.offload_schedule
concurrency_policy = "Forbid"
successful_jobs_history_limit = 3
failed_jobs_history_limit = 3
job_template {
metadata {
labels = local.labels
}
spec {
backoff_limit = 1
template {
metadata {
labels = local.labels
}
spec {
restart_policy = "Never"
# Step 1: copy Hive partitions verbatim (T1 layout == T3 layout)
init_container {
name = "copy"
image = "busybox:1.36"
command = ["sh", "-c", "cp -ru /lake/flight_id=* /warehouse/ 2>/dev/null; ls /warehouse | wc -l"]
volume_mount {
name = "lake"
mount_path = "/lake"
read_only = true
}
volume_mount {
name = "warehouse"
mount_path = "/warehouse"
}
}
# Step 2: mirror the warehouse into the object store for
# downstream consumers (training pipelines, replay tooling)
container {
name = "mirror"
image = "minio/mc:RELEASE.2024-06-12T14-34-03Z"
command = ["sh", "-c", "mc alias set store http://minio:9000 \"$MINIO_ROOT_USER\" \"$MINIO_ROOT_PASSWORD\" && mc mb -p store/warehouse && mc mirror --overwrite /warehouse store/warehouse"]
env_from {
secret_ref {
name = kubernetes_secret.minio.metadata[0].name
}
}
volume_mount {
name = "warehouse"
mount_path = "/warehouse"
read_only = true
}
}
volume {
name = "lake"
host_path {
path = var.lake_host_path
}
}
volume {
name = "warehouse"
host_path {
path = var.warehouse_host_path
type = "DirectoryOrCreate"
}
}
}
}
}
}
}
}
# --- Warehouse explorer: historical read-only SQL over all flights -------------
resource "kubernetes_deployment" "warehouse_explorer" {
metadata {
name = "warehouse-explorer"
namespace = kubernetes_namespace.ground.metadata[0].name
labels = local.labels
}
spec {
replicas = 1
selector {
match_labels = { app = "warehouse-explorer" }
}
template {
metadata {
labels = merge(local.labels, { app = "warehouse-explorer" })
}
spec {
container {
name = "explorer"
image = var.simulator_image
image_pull_policy = "Never"
command = ["python", "explorer/server.py"]
env {
name = "DATA_DIR"
value = "/warehouse"
}
port {
container_port = 8088
}
volume_mount {
name = "warehouse"
mount_path = "/warehouse"
read_only = true
}
}
volume {
name = "warehouse"
host_path {
path = var.warehouse_host_path
type = "DirectoryOrCreate"
}
}
}
}
}
}
resource "kubernetes_service" "warehouse_explorer" {
metadata {
name = "warehouse-explorer"
namespace = kubernetes_namespace.ground.metadata[0].name
}
spec {
type = "NodePort"
selector = { app = "warehouse-explorer" }
port {
port = 8088
target_port = 8088
node_port = var.node_ports.warehouse_explorer
}
}
}