arrproxy: v0.6.0 self-migrating, drop external migrate Job (#444)
## Why arrproxy v0.6.0 applies its own schema at startup under a Postgres advisory lock and holds `/readyz` until the schema is current, so every replica is safe to roll without an external gate. The wave-1 psql `arrproxy-migrate` Job and its SQL ConfigMap now only re-run idempotent statements the app already owns — dead weight, a second source of truth for the schema, and a standing drift trap whenever the app's embedded migrations move ahead of the manifests. ## How - Bump `arrproxy-api` and `arrproxy-ui` to `v0.6.0`. - Delete `migrate-job.yaml` and `migrations-configmap.yaml` and drop both from the arrproxy kustomization. - Keep the wave-0/wave-2 split: wave 2 still orders the api behind the wave-0 CNPG Cluster and VSO-synced Secrets, which is independent of the migrate Job; the stale "serve only after the wave-1 migrate Job" comment is corrected. - Rendered diff vs `main` is exactly the two image bumps plus the `arrproxy-migrate` Job and `arrproxy-migrations` ConfigMap disappearing; `kustomize build --enable-helm apps/overlays/au-syd1/arrstack` and pre-commit both clean. Reviewed-on: #444 Co-authored-by: unkin-agent <unkin-agent@unkin.net> Co-committed-by: unkin-agent <unkin-agent@unkin.net>
This commit was merged in pull request #444.
This commit is contained in:
@@ -5,7 +5,9 @@ metadata:
|
|||||||
name: arrproxy-api
|
name: arrproxy-api
|
||||||
namespace: arrstack
|
namespace: arrstack
|
||||||
annotations:
|
annotations:
|
||||||
# Wave 2: serve only after the wave-1 migrate Job completes.
|
# Wave 2: start only after the wave-0 CNPG Cluster and VSO-synced Secrets
|
||||||
|
# exist. The api self-migrates at startup under a Postgres advisory lock and
|
||||||
|
# holds /readyz until the schema is current, so no migration ordering is needed.
|
||||||
argocd.argoproj.io/sync-wave: "2"
|
argocd.argoproj.io/sync-wave: "2"
|
||||||
secret.reloader.stakater.com/reload: "arrproxy-pepper,arrproxy-admin-token,arrproxy-db-app,sonarr-adult-apikey,radarr-adult-apikey,sonarr-kids-apikey,radarr-kids-apikey"
|
secret.reloader.stakater.com/reload: "arrproxy-pepper,arrproxy-admin-token,arrproxy-db-app,sonarr-adult-apikey,radarr-adult-apikey,sonarr-kids-apikey,radarr-kids-apikey"
|
||||||
configmap.reloader.stakater.com/reload: "arrproxy-tiers"
|
configmap.reloader.stakater.com/reload: "arrproxy-tiers"
|
||||||
@@ -34,7 +36,7 @@ spec:
|
|||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
containers:
|
containers:
|
||||||
- name: api
|
- name: api
|
||||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-api:v0.5.0
|
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-api:v0.6.0
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ kind: Kustomization
|
|||||||
resources:
|
resources:
|
||||||
- cnpg_cluster.yaml
|
- cnpg_cluster.yaml
|
||||||
- cnpg_backup.yaml
|
- cnpg_backup.yaml
|
||||||
- migrations-configmap.yaml
|
|
||||||
- migrate-job.yaml
|
|
||||||
- vaultstaticsecret.yaml
|
- vaultstaticsecret.yaml
|
||||||
- tiers-configmap.yaml
|
- tiers-configmap.yaml
|
||||||
- oauth2-proxy-configmap.yaml
|
- oauth2-proxy-configmap.yaml
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
---
|
|
||||||
# Applies the arrproxy schema once per sync, before the api rolls, so the serve
|
|
||||||
# replicas never race migrations (arrproxy-api does not self-migrate). Runs as the
|
|
||||||
# CNPG-minted app user so the tokens table is owned by that role.
|
|
||||||
#
|
|
||||||
# Sync-phase hook at wave 1 (NOT PreSync): the CNPG Cluster + generated
|
|
||||||
# arrproxy-db-app Secret apply at wave 0 and ArgoCD waits for the Cluster to be
|
|
||||||
# Healthy before starting wave 1, so Postgres exists before migrate connects.
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: arrproxy-migrate
|
|
||||||
namespace: arrstack
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/hook: Sync
|
|
||||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
|
||||||
argocd.argoproj.io/sync-wave: "1"
|
|
||||||
spec:
|
|
||||||
backoffLimit: 6
|
|
||||||
ttlSecondsAfterFinished: 600
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: arrproxy-migrate
|
|
||||||
spec:
|
|
||||||
serviceAccountName: default
|
|
||||||
automountServiceAccountToken: false
|
|
||||||
restartPolicy: Never
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 65532
|
|
||||||
runAsGroup: 65532
|
|
||||||
fsGroup: 65532
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
containers:
|
|
||||||
- name: migrate
|
|
||||||
image: docker.io/library/postgres:18-alpine
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
env:
|
|
||||||
- name: HOME
|
|
||||||
value: /tmp
|
|
||||||
- name: PGUSER
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: arrproxy-db-app
|
|
||||||
key: username
|
|
||||||
- name: PGPASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: arrproxy-db-app
|
|
||||||
key: password
|
|
||||||
- name: PGHOST
|
|
||||||
value: arrproxy-db-rw.arrstack.svc.cluster.local
|
|
||||||
- name: PGPORT
|
|
||||||
value: "5432"
|
|
||||||
- name: PGDATABASE
|
|
||||||
value: arrproxy
|
|
||||||
- name: PGSSLMODE
|
|
||||||
value: require
|
|
||||||
command:
|
|
||||||
- psql
|
|
||||||
- -v
|
|
||||||
- ON_ERROR_STOP=1
|
|
||||||
- -f
|
|
||||||
- /migrations/0001_init.sql
|
|
||||||
- -f
|
|
||||||
- /migrations/0002_tier_tokens.sql
|
|
||||||
- -f
|
|
||||||
- /migrations/0003_token_methods.sql
|
|
||||||
volumeMounts:
|
|
||||||
- name: migrations
|
|
||||||
mountPath: /migrations
|
|
||||||
readOnly: true
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp
|
|
||||||
securityContext:
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
capabilities:
|
|
||||||
drop:
|
|
||||||
- ALL
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 256Mi
|
|
||||||
volumes:
|
|
||||||
- name: migrations
|
|
||||||
configMap:
|
|
||||||
name: arrproxy-migrations
|
|
||||||
- name: tmp
|
|
||||||
emptyDir:
|
|
||||||
sizeLimit: 64Mi
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
# arrproxy schema, mirrored from the arrproxy repo migrations/ (v0.5.0).
|
|
||||||
# arrproxy-api does NOT self-migrate, so the wave-1 migrate Job applies these in
|
|
||||||
# order once per sync as the app user. Every statement is idempotent, so a resync
|
|
||||||
# over an already-migrated database is a no-op. Keep in sync with the repo on
|
|
||||||
# schema bumps.
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: arrproxy-migrations
|
|
||||||
namespace: arrstack
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "0"
|
|
||||||
data:
|
|
||||||
0001_init.sql: |
|
|
||||||
-- arrproxy token store. Only token hashes are persisted; plaintext is shown
|
|
||||||
-- once at mint time and never recoverable.
|
|
||||||
CREATE TABLE IF NOT EXISTS tokens (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
subject TEXT NOT NULL,
|
|
||||||
label TEXT NOT NULL DEFAULT '',
|
|
||||||
token_hash TEXT NOT NULL UNIQUE,
|
|
||||||
apps TEXT[] NOT NULL DEFAULT '{}',
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
expires_at TIMESTAMPTZ,
|
|
||||||
disabled BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
last_used_at TIMESTAMPTZ
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS tokens_subject_idx ON tokens (subject);
|
|
||||||
CREATE INDEX IF NOT EXISTS tokens_token_hash_idx ON tokens (token_hash);
|
|
||||||
0002_tier_tokens.sql: |
|
|
||||||
-- Tier-scoped virtual API keys. Existing rows (tier '') remain legacy per-app
|
|
||||||
-- tokens validated on the unprefixed routes; tier keys carry a tier name and,
|
|
||||||
-- for read-only tiers (kids), read_only=true so writes are rejected.
|
|
||||||
ALTER TABLE tokens ADD COLUMN IF NOT EXISTS tier TEXT NOT NULL DEFAULT '';
|
|
||||||
ALTER TABLE tokens ADD COLUMN IF NOT EXISTS read_only BOOLEAN NOT NULL DEFAULT false;
|
|
||||||
0003_token_methods.sql: |
|
|
||||||
-- Per-token HTTP method scoping. An empty list (the default every existing row
|
|
||||||
-- gets) means unrestricted, so tokens minted before this column behave exactly
|
|
||||||
-- as before; a non-empty list limits the token to those methods.
|
|
||||||
ALTER TABLE tokens ADD COLUMN IF NOT EXISTS methods TEXT[] NOT NULL DEFAULT '{}';
|
|
||||||
@@ -31,7 +31,7 @@ spec:
|
|||||||
type: RuntimeDefault
|
type: RuntimeDefault
|
||||||
containers:
|
containers:
|
||||||
- name: ui
|
- name: ui
|
||||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-ui:v0.5.0
|
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/arrproxy-ui:v0.6.0
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
|||||||
Reference in New Issue
Block a user