diff --git a/.github/workflows/build-push-podstatus.yaml b/.github/workflows/build-push-podstatus.yaml new file mode 100644 index 0000000..eb0ade0 --- /dev/null +++ b/.github/workflows/build-push-podstatus.yaml @@ -0,0 +1,76 @@ +name: Build and Push Pod Status + +on: + push: + branches: + - main + paths: + - Dockerfile.podstatus + - poetry.lock + - pyproject.toml + - podstatus/**/* + - .github/workflows/build-push-podstatus.yaml + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}-podstatus + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + attestations: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + install: true + driver-opts: | + image=moby/buildkit:latest + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Build and Push Docker image + id: push + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile.podstatus + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true diff --git a/Dockerfile.podstatus b/Dockerfile.podstatus new file mode 100644 index 0000000..7bf5ae4 --- /dev/null +++ b/Dockerfile.podstatus @@ -0,0 +1,37 @@ +### BUILDER +FROM python:3.12-bookworm as builder + +RUN pip install poetry==1.8.3 + +ENV POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_IN_PROJECT=1 \ + POETRY_VIRTUALENVS_CREATE=1 \ + POETRY_CACHE_DIR=/tmp/poetry_cache + +WORKDIR /app + +COPY pyproject.toml poetry.lock ./ +RUN touch README.md + +RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --no-root + +### RUNTIME +FROM python:3.12-bookworm as runtime + +RUN apt-get update && \ + apt-get install -y nginx && \ + rm -rf /var/lib/apt/lists/* + +ENV VIRTUAL_ENV=/app/.venv \ + PATH="/app/.venv/bin:$PATH" \ + PYTHONPATH="/conferenceli" \ + GUNICORN_CMD_ARGS="--workers=1 --bind=unix:/app/gunicorn.sock --access-logfile=-" + +COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV} + +COPY podstatus ./conferenceli +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 8080 + +CMD ["sh", "-c", "nginx && gunicorn conferenceli.app:app"] diff --git a/nginx.conf b/nginx.conf index 384dab5..4f10031 100644 --- a/nginx.conf +++ b/nginx.conf @@ -29,7 +29,7 @@ http { keepalive_timeout 5; # path for static files - root /contactform; + root /conferenceli; location / { # checks for static file, if not found proxy to app diff --git a/podstatus/app.py b/podstatus/app.py index fcec935..2226c65 100644 --- a/podstatus/app.py +++ b/podstatus/app.py @@ -1,12 +1,15 @@ import logging import os +import sys import random import time from kubernetes import client, config as k8sconfig, watch +from kubernetes.config import ConfigException from flask import Flask, render_template, Response, jsonify from flask_bootstrap import Bootstrap5 from threading import Thread, Event from blinkstick import blinkstick +from usb.core import NoBackendError from config import * @@ -20,22 +23,29 @@ # Load kubeconfig or use in-cluster configuration kubeconfig_path = config.KUBECONFIG -if kubeconfig_path: - k8sconfig.load_kube_config(config_file=kubeconfig_path) -else: - k8sconfig.load_incluster_config() +try: + if kubeconfig_path: + k8sconfig.load_kube_config(config_file=kubeconfig_path) + else: + k8sconfig.load_incluster_config() + + v1 = client.CoreV1Api() + namespace = config.K8S_NAMESPACE +except ConfigException as e: + logging.fatal(e) + sys.exit(4) + +# Blinkstick setup +try: + bstick = blinkstick.find_first() + led_per_pod = 3 +except NoBackendError as e: + logging.fatal(f"BlinkStick setup failed: {e}") -# Kubernetes setup -v1 = client.CoreV1Api() -namespace = config.K8S_NAMESPACE # Threading setup stop_event = Event() -# Blinkstick setup -bstick = blinkstick.find_first() -led_per_pod = 3 - def set_led_color(pod_index, color): start_led = pod_index * led_per_pod