variable "tls_secret_name" { type = string sensitive = true } variable "nfs_server" { type = string } variable "discord_f1_guild_id" { type = string } variable "discord_f1_channel_ids" { type = string } resource "kubernetes_namespace" "f1-stream" { metadata { name = "f1-stream" labels = { "istio-injection" : "disabled" tier = local.tiers.aux "chrome-service.viktorbarzin.me/client" = "true" "keel.sh/enrolled" = "true" } } lifecycle { # KYVERNO_LIFECYCLE_V1: goldilocks-vpa-auto-mode ClusterPolicy stamps this label on every namespace ignore_changes = [metadata[0].labels["goldilocks.fairwinds.com/vpa-update-mode"]] } } resource "kubernetes_manifest" "external_secret" { manifest = { apiVersion = "external-secrets.io/v1beta1" kind = "ExternalSecret" metadata = { name = "f1-stream-secrets" namespace = "f1-stream" } spec = { refreshInterval = "15m" secretStoreRef = { name = "vault-kv" kind = "ClusterSecretStore" } target = { name = "f1-stream-secrets" } dataFrom = [{ extract = { key = "f1-stream" } }] } } depends_on = [kubernetes_namespace.f1-stream] } # Pull the chrome-service bearer token into this namespace as a separate # Secret so the verifier can reach the in-cluster Playwright pool. resource "kubernetes_manifest" "chrome_service_client_secret" { manifest = { apiVersion = "external-secrets.io/v1beta1" kind = "ExternalSecret" metadata = { name = "chrome-service-client-secrets" namespace = "f1-stream" } spec = { refreshInterval = "15m" secretStoreRef = { name = "vault-kv" kind = "ClusterSecretStore" } target = { name = "chrome-service-client-secrets" } dataFrom = [{ extract = { key = "chrome-service" } }] } } depends_on = [kubernetes_namespace.f1-stream] } module "nfs_data_host" { source = "../../modules/kubernetes/nfs_volume" name = "f1-stream-data-host" namespace = kubernetes_namespace.f1-stream.metadata[0].name nfs_server = var.nfs_server nfs_path = "/srv/nfs/f1-stream" storage = "1Gi" access_modes = ["ReadWriteOnce"] } resource "kubernetes_deployment" "f1-stream" { metadata { name = "f1-stream" namespace = kubernetes_namespace.f1-stream.metadata[0].name labels = { app = "f1-stream" tier = local.tiers.aux } annotations = { "reloader.stakater.com/auto" = "true" } } spec { replicas = 1 strategy { type = "Recreate" } selector { match_labels = { app = "f1-stream" } } template { metadata { labels = { app = "f1-stream" } } spec { container { image = "viktorbarzin/f1-stream:latest" image_pull_policy = "Always" name = "f1-stream" resources { limits = { memory = "1Gi" } requests = { cpu = "100m" memory = "1Gi" } } port { container_port = 8000 } env { name = "DISCORD_TOKEN" value_from { secret_key_ref { name = "f1-stream-secrets" key = "discord_user_token" } } } env { name = "DISCORD_CHANNELS" value = var.discord_f1_channel_ids } # Verifier connects to in-cluster headed Chromium pool — see # stacks/chrome-service/. Falls back to in-process headless if unset. env { name = "CHROME_WS_URL" value = "ws://chrome-service.chrome-service.svc.cluster.local:3000" } env { name = "CHROME_WS_TOKEN" value_from { secret_key_ref { name = "chrome-service-client-secrets" key = "api_bearer_token" } } } # The embed proxy (this pod's /embed?url=…) must be reachable from # the remote chrome-service pod. Default 127.0.0.1 only works for # in-process Chromium — for the remote browser we point it at our # own ClusterIP service. env { name = "PLAYBACK_VERIFY_PROXY_BASE" value = "http://f1.f1-stream.svc.cluster.local" } volume_mount { name = "data" mount_path = "/data" } } volume { name = "data" persistent_volume_claim { claim_name = module.nfs_data_host.claim_name } } } } } lifecycle { ignore_changes = [ spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1 metadata[0].annotations["keel.sh/policy"], metadata[0].annotations["keel.sh/trigger"], metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2 metadata[0].annotations["keel.sh/match-tag"], spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates metadata[0].annotations["kubernetes.io/change-cause"], metadata[0].annotations["deployment.kubernetes.io/revision"], spec[0].template[0].metadata[0].annotations["keel.sh/update-time"], # KEEL_LIFECYCLE_V1 ] } } resource "kubernetes_service" "f1-stream" { metadata { name = "f1" namespace = kubernetes_namespace.f1-stream.metadata[0].name labels = { "app" = "f1-stream" } } spec { selector = { app = "f1-stream" } port { port = "80" target_port = "8000" } } } module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.f1-stream.metadata[0].name tls_secret_name = var.tls_secret_name } # f1-stream serves its SvelteKit SPA via the FastAPI `/{path}` catch-all # and exposes 14 JSON/proxy routes at root (/schedule, /streams, /embed, # /embed-asset, /relay, /proxy, /extract, /extractors, /health). A flat # Anubis catch-all CHALLENGE breaks the SPA's XHRs with "Unexpected token # '<', '