migrate-job.yaml 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. # Exactly-once schema migration Job (task 46).
  2. #
  3. # Run BEFORE the Deployment rolls (CI applies the Job, waits for completion,
  4. # then rolls the Deployment). `migrate` applies forward-only sqlx migrations
  5. # under Postgres advisory lock, so concurrent starts serialize and the schema
  6. # is applied exactly once — replicas MUST NOT migrate (they share one
  7. # database; per-replica migration races and double-applies). The Deployment's
  8. # readiness gate (`_sqlx_migrations` version match) keeps new replicas out of
  9. # traffic until this Job succeeds.
  10. #
  11. # Hardened like the Deployment: non-root 65532,
  12. # read-only rootfs, ALL capabilities dropped, `automountServiceAccountToken:
  13. # false`, and every secret (token pepper, TOTP key, mandatory PII key, DB/Redis
  14. # passwords) mounted as 0400 tmpfs files under `/run/secrets` (read through the
  15. # `*_FILE` variables) instead of env vars. The image carries the same placeholder
  16. # digest; `pin-digest` rewrites the real digest in `kustomization.yaml` for
  17. # every workload.
  18. apiVersion: batch/v1
  19. kind: Job
  20. metadata:
  21. name: d-oauth-migrate
  22. namespace: d-oauth
  23. labels:
  24. app.kubernetes.io/name: d-oauth-server
  25. app.kubernetes.io/part-of: d-oauth
  26. spec:
  27. backoffLimit: 3
  28. activeDeadlineSeconds: 300
  29. ttlSecondsAfterFinished: 3600
  30. template:
  31. metadata:
  32. labels:
  33. app.kubernetes.io/name: d-oauth-server
  34. app.kubernetes.io/part-of: d-oauth
  35. app.kubernetes.io/component: migrate
  36. spec:
  37. restartPolicy: Never
  38. automountServiceAccountToken: false
  39. securityContext:
  40. runAsNonRoot: true
  41. runAsUser: 65532
  42. runAsGroup: 65532
  43. fsGroup: 65532
  44. fsGroupChangePolicy: OnRootMismatch
  45. seccompProfile:
  46. type: RuntimeDefault
  47. containers:
  48. - name: migrate
  49. # Same pinning rule as the Deployment: placeholder digest, never a
  50. # shippable `:latest`; CI resolves it via `kustomization.yaml`.
  51. image: ghcr.io/syavne/d-oauth-server@sha256:0000000000000000000000000000000000000000000000000000000000000000
  52. imagePullPolicy: IfNotPresent
  53. # Overrides the image ENTRYPOINT (["/d-oauth-server","serve"]).
  54. command: ["/d-oauth-server", "migrate"]
  55. envFrom:
  56. - configMapRef:
  57. name: d-oauth-config
  58. env:
  59. # Same file-only secret rule as the Deployment; the DSNs come from
  60. # the ConfigMap.
  61. - name: D_OAUTH_DATABASE__PASSWORD_FILE
  62. value: /run/secrets/database-password
  63. - name: D_OAUTH_REDIS__PASSWORD_FILE
  64. value: /run/secrets/redis-password
  65. - name: D_OAUTH_TOKEN_PEPPER_FILE
  66. value: /run/secrets/token-pepper
  67. - name: D_OAUTH_TOTP_ENCRYPTION_KEY_FILE
  68. value: /run/secrets/totp-encryption-key
  69. - name: D_OAUTH_PII_ENCRYPTION_KEY_FILE
  70. value: /run/secrets/pii-encryption-key
  71. resources:
  72. requests:
  73. cpu: 50m
  74. memory: 64Mi
  75. limits:
  76. cpu: 500m
  77. memory: 256Mi
  78. securityContext:
  79. allowPrivilegeEscalation: false
  80. readOnlyRootFilesystem: true
  81. capabilities:
  82. drop:
  83. - ALL
  84. volumeMounts:
  85. - name: certs
  86. mountPath: /certs
  87. readOnly: true
  88. - name: secrets
  89. mountPath: /run/secrets
  90. readOnly: true
  91. volumes:
  92. - name: certs
  93. secret:
  94. secretName: d-oauth-tls
  95. defaultMode: 0o400
  96. - name: secrets
  97. secret:
  98. secretName: d-oauth-secrets
  99. defaultMode: 0o400
  100. items:
  101. - key: database-password
  102. path: database-password
  103. - key: redis-password
  104. path: redis-password
  105. - key: token-pepper
  106. path: token-pepper
  107. - key: totp-encryption-key
  108. path: totp-encryption-key
  109. - key: pii-encryption-key
  110. path: pii-encryption-key