# One-shot migration Job. Runs goose against Neon's *direct* (non-pooler) # endpoint, applies any pending migrations from /app/migrations (baked into # the api image), exits. # # 03-deploy.sh deletes any prior Job, applies this one, waits for completion # with `kubectl wait --for=condition=complete`, and rolls api/worker only # after the Job succeeds. A Job failure aborts the whole deploy. # # We reuse the api image rather than build a separate one — the api Dockerfile # already installs the goose CLI to /usr/local/bin/goose and copies the # migrations directory to /app/migrations. apiVersion: batch/v1 kind: Job metadata: name: honeydue-migrate namespace: honeydue labels: app.kubernetes.io/name: migrate app.kubernetes.io/part-of: honeydue spec: backoffLimit: 0 # fail fast — no silent retries on a bad migration ttlSecondsAfterFinished: 86400 # keep finished Job for 24h so logs are inspectable template: metadata: labels: app.kubernetes.io/name: migrate app.kubernetes.io/part-of: honeydue spec: restartPolicy: Never # The migrate Job never calls the k8s API (audit F11). automountServiceAccountToken: false imagePullSecrets: - name: gitea-credentials securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 seccompProfile: type: RuntimeDefault containers: - name: goose image: IMAGE_PLACEHOLDER # Replaced by 03-deploy.sh — same as api imagePullPolicy: IfNotPresent # audit CODE-L4 — explicit command: ["/bin/sh", "-c"] # DB_HOST in the ConfigMap points at the -pooler endpoint for runtime. # goose's session-scoped advisory lock can't survive PgBouncer # transaction-mode, so we strip the -pooler segment for migrations. # `set -e` so any sub-command failure exits non-zero. args: - | set -e DIRECT_HOST=$(echo "$DB_HOST" | sed 's/-pooler\.\(.*\)$/.\1/') echo "[migrate] running goose up against $DIRECT_HOST" exec /usr/local/bin/goose \ -dir /app/migrations \ postgres "host=$DIRECT_HOST port=$DB_PORT user=$POSTGRES_USER password=$POSTGRES_PASSWORD dbname=$POSTGRES_DB sslmode=$DB_SSLMODE" \ up securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] envFrom: - configMapRef: name: honeydue-config env: - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: honeydue-secrets key: POSTGRES_PASSWORD resources: requests: cpu: 100m memory: 64Mi limits: cpu: 500m memory: 256Mi