# Simulated fleet (T4 dev environment, docs/06) on the k3d cluster created # by infra/ansible/sim-cluster.yml. Drones are StatefulSet replicas writing # to a shared hostPath lake — the same /data contract as the on-board NVMe. locals { labels = { "app.kubernetes.io/part-of" = "swarm-sim" } } resource "kubernetes_namespace" "swarm" { metadata { name = var.namespace labels = local.labels } } # --- Fleet ------------------------------------------------------------------ resource "kubernetes_stateful_set" "drone" { metadata { name = "drone" namespace = kubernetes_namespace.swarm.metadata[0].name labels = local.labels } spec { service_name = "drone" replicas = var.drone_count # A landed drone powers off; the next start is the next flight pod_management_policy = "Parallel" selector { match_labels = { app = "drone" } } template { metadata { labels = merge(local.labels, { app = "drone" }) } spec { container { name = "drone" image = var.simulator_image image_pull_policy = "Never" # imported via k3d image import env { name = "DRONE_ID" value_from { field_ref { field_path = "metadata.name" } } } env { name = "DURATION_S" value = tostring(var.flight_duration_s) } env { name = "SPEEDUP" value = tostring(var.speedup) } env { name = "DATA_DIR" value = "/data" } volume_mount { name = "lake" mount_path = "/data" } resources { requests = { cpu = "50m", memory = "128Mi" } limits = { cpu = "500m", memory = "512Mi" } } } volume { name = "lake" host_path { path = var.data_host_path } } } } } } # --- Exporter + Prometheus + Grafana (observability, docs/07) ---------------- resource "kubernetes_deployment" "exporter" { metadata { name = "exporter" namespace = kubernetes_namespace.swarm.metadata[0].name labels = local.labels } spec { replicas = 1 selector { match_labels = { app = "exporter" } } template { metadata { labels = merge(local.labels, { app = "exporter" }) } spec { container { name = "exporter" image = var.simulator_image image_pull_policy = "Never" command = ["python", "monitoring/exporter.py"] env { name = "DATA_DIR" value = "/data" } env { name = "EXPORTER_PORT" value = "9105" } port { container_port = 9105 } volume_mount { name = "lake" mount_path = "/data" read_only = true } } volume { name = "lake" host_path { path = var.data_host_path } } } } } } resource "kubernetes_service" "exporter" { metadata { name = "exporter" namespace = kubernetes_namespace.swarm.metadata[0].name } spec { selector = { app = "exporter" } port { port = 9105 target_port = 9105 } } } resource "kubernetes_deployment" "explorer" { metadata { name = "explorer" namespace = kubernetes_namespace.swarm.metadata[0].name labels = local.labels } spec { replicas = 1 selector { match_labels = { app = "explorer" } } template { metadata { labels = merge(local.labels, { app = "explorer" }) } spec { container { name = "explorer" image = var.simulator_image image_pull_policy = "Never" command = ["python", "explorer/server.py"] env { name = "DATA_DIR" value = "/data" } env { name = "EXPLORER_PORT" value = "8088" } port { container_port = 8088 } volume_mount { name = "lake" mount_path = "/data" read_only = true } } volume { name = "lake" host_path { path = var.data_host_path } } } } } } resource "kubernetes_service" "explorer" { metadata { name = "explorer" namespace = kubernetes_namespace.swarm.metadata[0].name } spec { type = "NodePort" selector = { app = "explorer" } port { port = 8088 target_port = 8088 node_port = var.node_ports.explorer } } } resource "kubernetes_config_map" "prometheus" { metadata { name = "prometheus-config" namespace = kubernetes_namespace.swarm.metadata[0].name } data = { "prometheus.yml" = <<-EOT global: scrape_interval: 5s scrape_configs: - job_name: swarm static_configs: - targets: ["exporter:9105"] EOT } } resource "kubernetes_deployment" "prometheus" { metadata { name = "prometheus" namespace = kubernetes_namespace.swarm.metadata[0].name labels = local.labels } spec { replicas = 1 selector { match_labels = { app = "prometheus" } } template { metadata { labels = merge(local.labels, { app = "prometheus" }) } spec { container { name = "prometheus" image = "prom/prometheus:v2.53.0" port { container_port = 9090 } volume_mount { name = "config" mount_path = "/etc/prometheus" } } volume { name = "config" config_map { name = kubernetes_config_map.prometheus.metadata[0].name } } } } } } resource "kubernetes_service" "prometheus" { metadata { name = "prometheus" namespace = kubernetes_namespace.swarm.metadata[0].name } spec { type = "NodePort" selector = { app = "prometheus" } port { port = 9090 target_port = 9090 node_port = var.node_ports.prometheus } } } resource "kubernetes_config_map" "grafana_provisioning" { metadata { name = "grafana-provisioning" namespace = kubernetes_namespace.swarm.metadata[0].name } data = { "datasource.yml" = <<-EOT apiVersion: 1 deleteDatasources: - name: Prometheus orgId: 1 datasources: - name: Prometheus uid: swarm-prom type: prometheus access: proxy url: http://prometheus:9090 isDefault: true EOT "dashboards.yml" = <<-EOT apiVersion: 1 providers: - name: swarm folder: "" type: file options: path: /var/lib/grafana/dashboards EOT } } resource "kubernetes_config_map" "grafana_dashboard" { metadata { name = "grafana-dashboard" namespace = kubernetes_namespace.swarm.metadata[0].name } data = { "swarm.json" = file("${path.module}/../../../simulator/monitoring/grafana/dashboards/swarm.json") } } resource "kubernetes_deployment" "grafana" { metadata { name = "grafana" namespace = kubernetes_namespace.swarm.metadata[0].name labels = local.labels } spec { replicas = 1 selector { match_labels = { app = "grafana" } } template { metadata { labels = merge(local.labels, { app = "grafana" }) } spec { container { name = "grafana" image = "grafana/grafana:11.1.0" port { container_port = 3000 } env { name = "GF_AUTH_ANONYMOUS_ENABLED" value = "true" } env { name = "GF_AUTH_ANONYMOUS_ORG_ROLE" value = "Admin" } env { name = "GF_AUTH_DISABLE_LOGIN_FORM" value = "true" } # Air-gap hygiene — same flags as the Compose profile env { name = "GF_ANALYTICS_REPORTING_ENABLED" value = "false" } env { name = "GF_ANALYTICS_CHECK_FOR_UPDATES" value = "false" } env { name = "GF_ANALYTICS_CHECK_FOR_PLUGIN_UPDATES" value = "false" } volume_mount { name = "provisioning-datasources" mount_path = "/etc/grafana/provisioning/datasources" } volume_mount { name = "provisioning-dashboards" mount_path = "/etc/grafana/provisioning/dashboards" } volume_mount { name = "dashboards" mount_path = "/var/lib/grafana/dashboards" } } volume { name = "provisioning-datasources" config_map { name = kubernetes_config_map.grafana_provisioning.metadata[0].name items { key = "datasource.yml" path = "datasource.yml" } } } volume { name = "provisioning-dashboards" config_map { name = kubernetes_config_map.grafana_provisioning.metadata[0].name items { key = "dashboards.yml" path = "dashboards.yml" } } } volume { name = "dashboards" config_map { name = kubernetes_config_map.grafana_dashboard.metadata[0].name } } } } } } resource "kubernetes_service" "grafana" { metadata { name = "grafana" namespace = kubernetes_namespace.swarm.metadata[0].name } spec { type = "NodePort" selector = { app = "grafana" } port { port = 3000 target_port = 3000 node_port = var.node_ports.grafana } } }