C1: bake shower runtime env into image; allow tailnet host

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 <cmd>` 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) <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-05-11 10:46:28 -07:00
commit 95b663209f
2 changed files with 20 additions and 8 deletions

View file

@ -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/<token>/`.
# /host/'s "Django admin" link follows DJANGO_ADMIN_URL.

View file

@ -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 <subcommand>` 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" = { };