apiVersion: batch/v1
kind: CronJob
metadata:
  name: veylant-postgres-backup
  namespace: veylant
  labels:
    app.kubernetes.io/name: veylant-postgres-backup
    app.kubernetes.io/component: backup
spec:
  # Run daily at 02:00 UTC — off-peak for EU West.
  schedule: "0 2 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 7
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      # Retry once on failure before marking as failed.
      backoffLimit: 1
      template:
        metadata:
          labels:
            app.kubernetes.io/name: veylant-postgres-backup
          annotations:
            # Vault Agent Injector — inject secrets from Vault.
            vault.hashicorp.com/agent-inject: "true"
            vault.hashicorp.com/role: "veylant-backup"
            vault.hashicorp.com/agent-inject-secret-db: "secret/veylant/production/db"
            vault.hashicorp.com/agent-inject-template-db: |
              {{- with secret "secret/veylant/production/db" -}}
              export PGPASSWORD="{{ .Data.data.password }}"
              export PGUSER="{{ .Data.data.username }}"
              export PGHOST="{{ .Data.data.host }}"
              export PGDATABASE="{{ .Data.data.dbname }}"
              {{- end }}
            vault.hashicorp.com/agent-inject-secret-aws: "secret/veylant/production/aws"
            vault.hashicorp.com/agent-inject-template-aws: |
              {{- with secret "secret/veylant/production/aws" -}}
              export AWS_ACCESS_KEY_ID="{{ .Data.data.access_key_id }}"
              export AWS_SECRET_ACCESS_KEY="{{ .Data.data.secret_access_key }}"
              export AWS_DEFAULT_REGION="{{ .Data.data.region }}"
              {{- end }}
        spec:
          restartPolicy: OnFailure
          serviceAccountName: veylant-backup
          securityContext:
            runAsNonRoot: true
            runAsUser: 999
            fsGroup: 999
          containers:
            - name: pg-backup
              image: postgres:16-alpine
              imagePullPolicy: IfNotPresent
              resources:
                requests:
                  cpu: 100m
                  memory: 128Mi
                limits:
                  cpu: 500m
                  memory: 256Mi
              env:
                - name: S3_BUCKET
                  value: "veylant-backups-production"
                - name: BACKUP_PREFIX
                  value: "postgres"
              command:
                - /bin/sh
                - -c
                - |
                  set -euo pipefail

                  # Load secrets injected by Vault Agent.
                  source /vault/secrets/db
                  source /vault/secrets/aws

                  # Install AWS CLI (not in postgres:16-alpine by default).
                  apk add --no-cache aws-cli 2>/dev/null || true

                  TIMESTAMP=$(date -u +"%Y%m%d_%H%M%S")
                  FILENAME="${BACKUP_PREFIX}_${TIMESTAMP}.sql.gz"
                  S3_PATH="s3://${S3_BUCKET}/${BACKUP_PREFIX}/${FILENAME}"

                  echo "[$(date -u)] Starting backup: ${FILENAME}"

                  # Dump and compress — pipe directly to S3 without storing locally.
                  pg_dump \
                    --host="${PGHOST}" \
                    --username="${PGUSER}" \
                    --dbname="${PGDATABASE}" \
                    --format=plain \
                    --no-password \
                    --verbose \
                  | gzip -9 \
                  | aws s3 cp - "${S3_PATH}" \
                      --storage-class STANDARD_IA \
                      --metadata "created-by=veylant-backup,db=${PGDATABASE}"

                  echo "[$(date -u)] Backup completed: ${S3_PATH}"

                  # Verify the upload is readable.
                  aws s3 ls "${S3_PATH}" || { echo "Upload verification failed"; exit 1; }

                  echo "[$(date -u)] Backup verified successfully."

---
# S3 Lifecycle policy is managed in Terraform (deploy/terraform/main.tf).
# Retention: 7 daily backups kept automatically via S3 lifecycle rules.
# Manual restore: aws s3 cp s3://veylant-backups-production/postgres/<file> - | gunzip | psql

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: veylant-backup
  namespace: veylant
  labels:
    app.kubernetes.io/name: veylant-backup
  annotations:
    # AWS IRSA — IAM role for S3 write access (created in Terraform).
    eks.amazonaws.com/role-arn: "arn:aws:iam::ACCOUNT_ID:role/veylant-backup-role"
