variable "service_name" { description = "Service name for consistent naming" type = string default = "ilsp" } variable "image_tag" { description = "Docker image tag — override in CI with commit SHA: -var=\"image_tag=$SHA\"" type = string default = "latest" } job "ilsp" { region = "global" datacenters = ["dc1"] type = "service" meta { uuid = uuidv4() deployed_at = "[[ timeNowUTC ]]" service_name = var.service_name } update { stagger = "30s" max_parallel = 1 auto_revert = true progress_deadline = "25m" } group "ilsp-group" { count = 1 # Deploy specifically on the 'autobox.i80.dk' node constraint { attribute = "${node.unique.name}" value = "autobox.i80.dk" } # Zero-downtime update strategy: canary ensures new alloc is healthy # before old alloc is stopped. Both run briefly during transition. update { canary = 1 # Start 1 new alloc before stopping old auto_promote = true # Promote automatically when healthy min_healthy_time = "15s" healthy_deadline = "20m" progress_deadline = "25m" auto_revert = true } network { port "http" { } } reschedule { attempts = 5 interval = "10m" delay = "30s" delay_function = "exponential" max_delay = "120s" unlimited = false } # Volumes disabled for quick deployment # volume "ssl-certs" { # type = "host" # source = "certs" # read_only = true # } volume "refactor-data" { type = "host" source = "refactor-data" read_only = false } # Register the service with Consul service { provider = "consul" name = var.service_name port = "http" # Traefik-specific tags for routing tags = [ "traefik.enable=true", "traefik.http.routers.${var.service_name}.rule=Host(`${var.service_name}.i80.dk`)", "traefik.http.routers.${var.service_name}.tls=true", # Rate limiting for refactoring operations "traefik.http.middlewares.${var.service_name}-limit.ratelimit.burst=10", "traefik.http.middlewares.${var.service_name}-limit.ratelimit.period=1m", "traefik.http.routers.${var.service_name}.middlewares=${var.service_name}-limit" ] # Primary health check - HTTP check { name = "http_health_check" type = "http" port = "http" path = "/health" interval = "10s" timeout = "5s" } } task "ilsp-task" { driver = "docker" config { image = "registry.i80.dk/gitea/ilsp:${var.image_tag}" ports = ["http"] force_pull = true auth { username = "robot$gitserver" password = "${HARBOR_ROBOT_TOKEN}" } } restart { attempts = 10 interval = "10m" delay = "15s" mode = "fail" } # Volume mounts disabled for quick deployment # volume_mount { # volume = "ssl-certs" # destination = "/certs" # read_only = true # } volume_mount { volume = "refactor-data" destination = "/app/data" read_only = false } env { # DevOpsMCP Configuration # Server Configuration PYTHONUNBUFFERED = "1" PORT = "${NOMAD_PORT_http}" HOST = "0.0.0.0" # Gitea (gea.i80.dk) API token for server-side CI/Actions queries GITEA_TOKEN = "441b0e1f3f23d2b29984c970743ec8f7fc4081fa" GITEA_URL = "https://gea.i80.dk" # LanguageTool — self-hosted grammar/spell-check (autobox.i80.dk:8010) LANGUAGETOOL_URL = "http://192.168.15.124:8010" # SSL certificate paths (available but not required for app) SSL_CERT_PATH = "/certs/wildcard.i80.dk.crt_cert.crt" SSL_KEY_PATH = "/certs/wildcard.i80.dk.key" SSL_FULLCHAIN_PATH = "/certs/wildcard.i80.dk.crt_fullchain.crt" # External MCP servers — tokens loaded from Consul (see template block below) # Servers start automatically in entrypoint.sh when tokens are present } # Registry authentication template template { data = <