From f3184405723e70ef2b687222bec389e65727ae15 Mon Sep 17 00:00:00 2001 From: Henrik Jess Nielsen Date: Sun, 5 Oct 2025 15:54:42 +0200 Subject: [PATCH] Nomad stuff --- .gitea/workflows/main.yml | 171 ++++++++++++++++++++++++++++---------- Dockerfile | 13 +-- app.py | 9 +- app/main.py | 5 ++ 4 files changed, 145 insertions(+), 53 deletions(-) diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml index 42a8f9a..a4552de 100644 --- a/.gitea/workflows/main.yml +++ b/.gitea/workflows/main.yml @@ -1,63 +1,150 @@ -name: Build, Push, and Deploy to Nomad +name: Build and Deploy LifeFAQ on: push: branches: - main + workflow_dispatch: jobs: - docker-nomad: - runs-on: self-hosted + build-image: + runs-on: debian-host + + env: + PATH: /usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/bin:/snap/bin + DOCKER_HOST: unix:///var/run/docker.sock + BUILDX_CONFIG: /tmp/buildx + steps: - - name: Check out code - uses: actions/checkout@v3 + - name: Checkout code + uses: actions/checkout@v4 - - name: Log in to Container Registry - run: echo ${{ secrets.password }} | docker login registry.i80.dk -u ${{ secrets.username }} --password-stdin + - name: System info + run: | + uname -a + whoami - - name: Build Docker Image - run: | - COMMIT_HASH=$(git rev-parse --short HEAD) - docker build -t registry.i80.dk/gitea/lifefaq:latest -t registry.i80.dk/gitea/lifefaq:${COMMIT_HASH} . + - name: Set up Docker Context for Buildx + id: buildx-context + run: | + export DOCKER_HOST=tcp://docker:2376/ + export DOCKER_TLS_VERIFY=0 + docker context rm builders || true + docker context create builders + - name: Verify Docker + run: docker --version - - name: Push Docker Image - run: | - COMMIT_HASH=$(git rev-parse --short HEAD) - echo "registry.i80.dk/gitea/lifefaq:latest" - echo "registry.i80.dk/gitea/lifefaq:${COMMIT_HASH}" - docker push registry.i80.dk/gitea/lifefaq:${COMMIT_HASH} - docker push registry.i80.dk/gitea/lifefaq:latest + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + env: + PATH: /usr/bin:/usr/local/bin:/bin:/sbin:/usr/sbin + - name: Log in to Docker Registry + run: | + echo "${{ secrets.HARBOR_ROBOT_TOKEN }}" | docker login registry.i80.dk -u "robot\$gitserver" --password-stdin + env: + PATH: /usr/bin:/usr/local/bin:/bin:/sbin:/usr/sbin - - name: Validate Nomad Job - env: - NOMAD_ADDR: https://nomad.i80.dk - run: nomad job validate .gitea/workflows/nomad-job.hcl + - name: Check for changes + id: changes + uses: dorny/paths-filter@v2 + with: + filters: | + docker: + - 'Dockerfile' + - 'app/**' + - 'requirements.txt' - - name: Stop old deployment - env: - NOMAD_ADDR: https://nomad.i80.dk - run: nomad job stop -purge -no-shutdown-delay lifefaq - continue-on-error: true + - name: Build and push Docker image + if: steps.changes.outputs.docker == 'true' + uses: docker/build-push-action@v5 + env: + PATH: /usr/bin:/usr/local/bin:/bin:/sbin:/usr/sbin + with: + context: . + file: ./Dockerfile + push: true + tags: | + registry.i80.dk/gitea/lifefaq:latest + - name: Test container health + run: | + echo "=== Starting container for health check ===" - - name: Apply Nomad Job - env: - NOMAD_ADDR: https://nomad.i80.dk - run: nomad job run .gitea/workflows/nomad-job.hcl + docker pull registry.i80.dk/gitea/lifefaq:latest - - name: Update Nginx Configuration - run: ssh runner@nomad sudo /opt/nginx_updater/venv/bin/python3 /opt/nginx_updater/nginx_updater.py lifefaq + CONTAINER_ID=$(docker run -d \ + -p 8000:8000 \ + -e PORT=8000 \ + -e APP_ENV=production \ + --name lifefaq-test \ + registry.i80.dk/gitea/lifefaq:latest) - - name: Update Forwarder Configuration - run: ssh runner@nomad sudo /opt/nginx_updater/venv/bin/python3 /opt/nginx_updater/update_forwarder.py --subdomain lifefaq + echo "Container started: ${CONTAINER_ID}" + echo "Waiting for /health endpoint..." + SUCCESS=false + for i in {1..90}; do + if curl -f -s http://localhost:8000/health > /dev/null 2>&1; then + echo "✓ Health check passed after ${i} seconds" + curl -s http://localhost:8000/health | jq '.' || echo "Health endpoint returned OK" + SUCCESS=true + break + fi + echo "Attempt ${i}/90 - waiting..." + sleep 1 + done -# - name: Restart Nomad Job -# env: -# NOMAD_ADDR: https://nomad.i80.dk -# run: | -# nomad job stop lifefaq -# sleep 5 # Optional: Wait to ensure the old allocation is stopped -# nomad job run .gitea/workflows/nomad-job.hcl + echo "=== Container Logs ===" + docker logs lifefaq-test + + docker stop lifefaq-test + docker rm lifefaq-test + + if [ "$SUCCESS" = false ]; then + echo "✗ Health check failed after 90 seconds" + exit 1 + fi + + echo "✓ Container health check passed - safe to deploy" + env: + PATH: /usr/bin:/usr/local/bin:/bin:/sbin:/usr/sbin + + - name: Deploy to Nomad + run: | + nomad job validate lifefaq.nomad + nomad job run lifefaq.nomad + env: + NOMAD_ADDR: "https://nomad.i80.dk:4646" + + - name: Wait for deployment + run: | + echo "Checking deployment status..." + nomad job status lifefaq + + echo "=== Allocation Details ===" + nomad job allocs lifefaq + + echo "=== Getting logs from allocations ===" + for alloc in $(nomad job allocs -all lifefaq | tail -n +2 | awk '{print $1}'); do + echo "Logs for allocation $alloc:" + + timeout=250 + SECONDS=0 + until nomad alloc logs "$alloc" 2>/dev/null || [ $SECONDS -gt $timeout ]; do + echo "Waiting for allocation to start... ($SECONDS/$timeout seconds)" + sleep 5 + done + + [ $SECONDS -gt $timeout ] && echo "Timeout for $alloc" + echo "---" + done + env: + NOMAD_ADDR: "https://nomad.i80.dk:4646" + + - name: Notify deployment status + run: | + echo "✅ Deployment completed!" + echo "LifeFAQ should be available at: https://lifefaq.i80.dk" + echo "Health check endpoint: https://lifefaq.i80.dk/health" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0f27692..2030405 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,13 @@ -# Base image with Python 3.11 FROM python:3.11-slim -# Set the working directory in the container WORKDIR /app -# Copy the requirements file to the working directory COPY requirements.txt . - -# Install Python dependencies RUN pip install --no-cache-dir -r requirements.txt -# Copy the rest of the application code COPY . . -# Expose the port the FastAPI app runs on (default Uvicorn port) -EXPOSE 9210 +# Port will be set via environment variable +EXPOSE 8000 -# Command to run the FastAPI application -CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "9210", "--workers", "1"] \ No newline at end of file +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "${PORT:-8000}", "--workers", "1"] \ No newline at end of file diff --git a/app.py b/app.py index 65c6c42..18982e4 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,12 @@ +import os import uvicorn from app.main import app if __name__ == "__main__": - uvicorn.run("app.main:app", host="0.0.0.0", port=9210, reload=False) + port = int(os.getenv("PORT", 8000)) + uvicorn.run( + "app.main:app", + host="0.0.0.0", + port=port, + reload=False + ) \ No newline at end of file diff --git a/app/main.py b/app/main.py index 769a018..f96d7ba 100644 --- a/app/main.py +++ b/app/main.py @@ -2,6 +2,7 @@ from fastapi import FastAPI from contextlib import asynccontextmanager from fastapi.staticfiles import StaticFiles +import app from app.controllers.route_to_web import RouteToWeb from app.services.markdown_processor import MarkdownProcessor from app.services.metadata_processor import MetadataProcessor @@ -68,6 +69,10 @@ class Application: """Return the FastAPI app instance.""" return self.app + @app.get("/health") + async def health(self): + return {"status": "healthy"} + application = Application() app = application.get_app()