Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions implementations/acr-control-plane/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ RUN pip install --no-cache-dir --no-index --find-links=/wheels acr-control-plane
COPY src/ ./src/
COPY policies/ ./policies/
COPY alembic.ini ./
# Migration entrypoint shared by the K8s init container, the docker-compose
# `acr-migrate` one-shot service, and CI. Installed on PATH so it can be
# invoked simply as `run-migrations` from any container using this image.
COPY scripts/run-migrations.sh /usr/local/bin/run-migrations
RUN chmod +x /usr/local/bin/run-migrations

ENV PYTHONPATH=/app/src
ENV PYTHONUNBUFFERED=1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,37 @@ spec:
matchLabels:
app: acr-gateway
topologyKey: kubernetes.io/hostname
# Apply database migrations before the gateway container starts.
# Uses the same image so we never have to maintain a separate
# migration build. Alembic is idempotent β€” replicas racing each
# other are safe (the loser sees the schema already at head).
initContainers:
- name: alembic-upgrade
image: acr-gateway:1.0.0
imagePullPolicy: IfNotPresent
command: ["run-migrations"]
envFrom:
- configMapRef:
name: acr-gateway-config
- secretRef:
name: acr-gateway-secret
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
volumeMounts:
- name: tmp
mountPath: /tmp
containers:
- name: gateway
image: acr-gateway:1.0.0
Expand Down
26 changes: 26 additions & 0 deletions implementations/acr-control-plane/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ services:
cpus: "0.1"
memory: 64M

# ── ACR Migrations (one-shot) ──────────────────────────
# Runs `alembic upgrade head` against the database, then exits cleanly.
# The gateway service waits for this to finish (service_completed_successfully)
# before starting, so the schema is always up-to-date and migrations cannot
# be forgotten when running the stack locally or in CI.
acr-migrate:
build:
context: .
dockerfile: Dockerfile
command: ["run-migrations"]
environment:
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-acr}:${POSTGRES_PASSWORD:-acr_dev_password}@postgres:5432/${POSTGRES_DB:-acr_control_plane}
ACR_ENV: ${ACR_ENV:-development}
LOG_LEVEL: ${LOG_LEVEL:-INFO}
depends_on:
postgres:
condition: service_healthy
restart: "no"
deploy:
resources:
limits:
cpus: "0.5"
memory: 256M

# ── ACR Gateway (main service) ─────────────────────────
acr-gateway:
build:
Expand Down Expand Up @@ -144,6 +168,8 @@ services:
condition: service_healthy
acr-killswitch:
condition: service_healthy
acr-migrate:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/acr/health"]
interval: 10s
Expand Down
36 changes: 36 additions & 0 deletions implementations/acr-control-plane/scripts/run-migrations.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env sh
# Apply all pending Alembic migrations and exit.
#
# Used by:
# * the K8s gateway initContainer (deploy/k8s/base/gateway-deployment.yaml)
# * the docker-compose `acr-migrate` one-shot service
# * CI / local development
#
# Idempotent: re-running on an up-to-date schema is a no-op. Multiple
# replicas racing each other are safe β€” Alembic wraps each migration in a
# transaction, so the loser of the race observes the schema as already at
# head and exits 0.

set -eu

# Resolve the application root regardless of where the script is invoked
# from. The runtime image stores the app at /app, but local devs may run
# this from anywhere inside the repo.
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
APP_ROOT="$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd)"

# /app inside the runtime image, repo path locally β€” both contain alembic.ini.
if [ -f "/app/alembic.ini" ]; then
APP_ROOT="/app"
fi

cd "${APP_ROOT}"

if [ -z "${DATABASE_URL:-}" ]; then
echo "[run-migrations] FATAL: DATABASE_URL is not set" >&2
exit 2
fi

echo "[run-migrations] applying alembic upgrade head against ${DATABASE_URL%%@*}@<redacted>"
alembic -c "${APP_ROOT}/alembic.ini" upgrade head
echo "[run-migrations] migrations complete"
Loading