Deploy sheet_sync as k8s CronJob (hourly) #439
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/basketball-api#439
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Feature
Lineage
Standalone — spawned from the westside-sheet-sync project scaffold on 2026-04-10. Depends on the sheet_sync service module being merged first.
Repo
forgejo_admin/pal-e-deploymentsUser Story
As ops
I want the sheet_sync service to run automatically every hour
So that Marcus's Google Sheet stays current without anyone having to trigger the sync manually
Ties to
story:sheet-sync.Context
The sheet_sync service module lives inside basketball-api but is not wired to any HTTP route. It's meant to be invoked by a scheduled job. The pal-e platform pattern is to deploy scheduled jobs as k8s CronJobs in the service's namespace, with the image being the same as the main service deployment.
The CronJob spec runs
python -m basketball_api.jobs.sheet_sync(a new thin entrypoint module that importssync_jersey_ordersand calls it with a SessionLocal). Exit code 0 = success, non-zero = failure. Logs go to stdout, picked up by Promtail → Loki.The CronJob needs the same
PGURLas the main basketball-api deployment, plus access to the new Google Sheets write token (mounted from a k8s secret, which is a separate concern this ticket creates).File Targets
Files to create:
bases/basketball-api/cronjob-sheet-sync.yaml— CronJob manifest. Schedule:0 * * * *(top of every hour). Uses the same image as the basketball-api Deployment, same env vars, one extra mount for the sheets write token.overlays/basketball-api/secret-sheets-token.enc.yaml— SOPS-encrypted secret containing the contents of~/secrets/google-oauth/sheets-westsidebasketball-write.json.Files to modify:
bases/basketball-api/kustomization.yaml— add the new CronJob to the resources list.overlays/basketball-api/kustomization.yaml— add the new secret to the resources list.src/basketball_api/jobs/sheet_sync.py(in basketball-api repo, not pal-e-deployments) — the new thin entrypoint. ~15 lines: import sessionmaker, call sync_jersey_orders, print result as JSON.Files NOT to touch:
Acceptance Criteria
kustomize build overlays/basketball-api/runs, then the output is a valid manifest list including the new CronJob and secret.sheet-syncappears in thebasketball-apinamespace.kubectl -n basketball-api create job --from=cronjob/sheet-sync sheet-sync-manualand tail the logs, then the job runs to completion with exit code 0.kubectl get jobs -n basketball-api.overlays/basketball-api/secret-sheets-token.enc.yaml, then the content matches~/secrets/google-oauth/sheets-westsidebasketball-write.json.Test Expectations
kustomize build overlays/basketball-api/ | kubectl apply --dry-run=server -f -succeeds.sops -d overlays/basketball-api/secret-sheets-token.enc.yamlreturns valid JSON.kubectl -n basketball-api create job --from=cronjob/sheet-sync sheet-sync-manual-$(date +%s)→ job completes with exit 0.kubectl get cronjob -n basketball-api sheet-sync -o jsonpath='{.spec.schedule}'returns0 * * * *.kustomize build overlays/basketball-api/ > /tmp/build.yaml && kubectl apply --dry-run=server -f /tmp/build.yamlConstraints
0 * * * *(hourly on the hour). Do NOT run more often than hourly without explicit justification — Google Sheets API has quotas.successfulJobsHistoryLimit: 3,failedJobsHistoryLimit: 3(avoid flooding job history).concurrencyPolicy: Forbid(never run two sheet_sync jobs concurrently).imagePullSecretas the basketball-api Deployment.Checklist
Related
westside-sheet-sync— projectstory-westside-jersey-sheet-sync— user story