From 95b663209f9d0b0dc6cb59c2002d4b35659d526e Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Mon, 11 May 2026 10:46:28 -0700 Subject: [PATCH] C1: bake shower runtime env into image; allow tailnet host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two complementary fixes for the deploy that just landed: 1. Pod was 0/1 Running because the readiness probe sends `Host: shower.ops.eblu.me` and the app's hardcoded ALLOWED_HOSTS only includes `shower.eblu.me`. settings.py exposes a DJANGO_ALLOWED_HOSTS env-var extras hook for exactly this case — wired into the configmap. 2. `kubectl exec deploy/shower -- python -m django ` returned "No module named django" because PYTHONPATH lived only inside the entrypoint script. Moved PYTHONPATH, DJANGO_SETTINGS_MODULE, PATH, and HOME into the image's Env block so exec'd shells inherit them. The entrypoint now just runs the boot sequence; the exports are redundant (image Env covers them) and gone. FOD inputs are unchanged so outputHash stays valid; no fakeHash dance. Co-Authored-By: Claude Opus 4.7 (1M context) --- argocd/manifests/shower/configmap.yaml | 6 ++++++ containers/shower/default.nix | 22 ++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/argocd/manifests/shower/configmap.yaml b/argocd/manifests/shower/configmap.yaml index 330e183..6102c1e 100644 --- a/argocd/manifests/shower/configmap.yaml +++ b/argocd/manifests/shower/configmap.yaml @@ -5,6 +5,12 @@ metadata: namespace: shower data: DJANGO_DEBUG: "0" + # The app's settings.py hardcodes ALLOWED_HOSTS = ["shower.eblu.me", + # "localhost", "127.0.0.1"] and exposes this env var as a comma-separated + # extras list. shower.ops.eblu.me is what Caddy on indri and the + # Tailscale ProxyGroup both send as the Host header, so the app needs to + # accept it. + DJANGO_ALLOWED_HOSTS: "shower.ops.eblu.me" # /host/, /admin/, and Django's login surface are all tailnet-only — the # public proxy 403s everything outside of `/` and `/prizes//`. # /host/'s "Django admin" link follows DJANGO_ADMIN_URL. diff --git a/containers/shower/default.nix b/containers/shower/default.nix index 20b7525..23539fb 100644 --- a/containers/shower/default.nix +++ b/containers/shower/default.nix @@ -154,26 +154,25 @@ let STATIC_ROOT = "/app/data/staticfiles" ''; + # PYTHONPATH, DJANGO_SETTINGS_MODULE, PATH, and HOME live in the image's + # `Env` block below — that way `kubectl exec deploy/shower -- python -m + # django ` Just Works without an inline `env` ceremony. + # The entrypoint just changes directory and runs the boot sequence. entrypoint = pkgs.writeShellScript "shower-entrypoint" '' set -eu - export HOME=/app/data - export PATH=${pyDeps}/bin:${python}/bin:/bin - export PYTHONPATH=/app:${sitePackages} - export DJANGO_SETTINGS_MODULE=local_settings - cd /app mkdir -p /app/data /app/media echo "shower: running migrations" - ${python}/bin/python -m django migrate --noinput + python -m django migrate --noinput echo "shower: collecting static files" - ${python}/bin/python -m django collectstatic --noinput --clear + python -m django collectstatic --noinput --clear echo "shower: starting gunicorn" - exec ${pyDeps}/bin/gunicorn \ + exec gunicorn \ --bind 0.0.0.0:8000 \ --workers 2 \ --forwarded-allow-ips='*' \ @@ -213,6 +212,13 @@ pkgs.dockerTools.buildLayeredImage { "LANG=C.UTF-8" "LC_ALL=C.UTF-8" "PYTHONDONTWRITEBYTECODE=1" + "HOME=/app/data" + "PATH=${pyDeps}/bin:${python}/bin:/bin" + # /app first so local_settings.py is importable; sitePackages second so + # django, gunicorn, etc. resolve. Inherited by entrypoint + any + # `kubectl exec` so manual django subcommands work without ceremony. + "PYTHONPATH=/app:${sitePackages}" + "DJANGO_SETTINGS_MODULE=local_settings" ]; ExposedPorts = { "8000/tcp" = { };