| 1 | --- |
| 2 | # SPDX-License-Identifier: AGPL-3.0-or-later |
| 3 | # Logical backup cron + cross-region Spaces sync. The actual scripts |
| 4 | # live under deploy/postgres/ and deploy/spaces/ so they can be |
| 5 | # read/edited without booting Ansible. |
| 6 | |
| 7 | - name: Backup scripts — install |
| 8 | copy: |
| 9 | src: "{{ playbook_dir }}/../{{ item.src }}" |
| 10 | dest: "{{ item.dest }}" |
| 11 | mode: "0755" |
| 12 | loop: |
| 13 | - { src: postgres/backup-daily.sh, dest: /usr/local/bin/shithub-backup-daily } |
| 14 | - { src: spaces/sync-cross-region.sh, dest: /usr/local/bin/shithub-spaces-sync } |
| 15 | |
| 16 | # Restore drill needs run.sh + smoke-queries.sql to live next to each |
| 17 | # other (run.sh reads the .sql via $(dirname "$0")). Put both in a |
| 18 | # share dir; the cron job invokes the absolute path. |
| 19 | - name: Restore drill — share dir |
| 20 | file: |
| 21 | path: /usr/local/share/shithub/restore-drill |
| 22 | state: directory |
| 23 | mode: "0755" |
| 24 | |
| 25 | - name: Restore drill — install |
| 26 | copy: |
| 27 | src: "{{ playbook_dir }}/../restore-drill/{{ item.src }}" |
| 28 | dest: "/usr/local/share/shithub/restore-drill/{{ item.src }}" |
| 29 | mode: "{{ item.mode }}" |
| 30 | loop: |
| 31 | - { src: run.sh, mode: "0755" } |
| 32 | - { src: smoke-queries.sql, mode: "0644" } |
| 33 | |
| 34 | # Single rclone config shared by every script that talks to Spaces: |
| 35 | # - root-owned for the daily/cross-region cron jobs (running as root) |
| 36 | # - postgres-readable (group 0640) for archive_command, which Postgres |
| 37 | # invokes as the postgres user. The previous /root/.config/rclone/ |
| 38 | # path was unreachable to postgres because /root is mode 0700; |
| 39 | # moving to /etc/ with explicit group sidesteps the traversal block. |
| 40 | - name: rclone config — Spaces credentials, shared with postgres |
| 41 | template: |
| 42 | src: rclone.conf.j2 |
| 43 | dest: /etc/rclone-shithub.conf |
| 44 | owner: root |
| 45 | group: postgres |
| 46 | mode: "0640" |
| 47 | |
| 48 | - name: cron — daily logical backup |
| 49 | cron: |
| 50 | name: shithub-backup-daily |
| 51 | job: /usr/local/bin/shithub-backup-daily >> /var/log/shithub-backup.log 2>&1 |
| 52 | minute: "17" |
| 53 | hour: "3" |
| 54 | |
| 55 | - name: cron — hourly cross-region sync |
| 56 | cron: |
| 57 | name: shithub-spaces-sync |
| 58 | job: /usr/local/bin/shithub-spaces-sync >> /var/log/shithub-spaces-sync.log 2>&1 |
| 59 | minute: "23" |
| 60 | |
| 61 | # Weekly restore drill. The runbook still calls for a *manual* drill |
| 62 | # every quarter (the operator inspects the result), but exercising |
| 63 | # the recovery path weekly catches backup-pipeline regressions |
| 64 | # (corrupt dumps, missing schema migrations) within days instead of |
| 65 | # months. The script logs to /var/log/shithub/restore-drill.log so |
| 66 | # the operator can `tail` it whenever. |
| 67 | - name: cron — weekly restore drill |
| 68 | cron: |
| 69 | name: shithub-restore-drill |
| 70 | job: /usr/local/share/shithub/restore-drill/run.sh |
| 71 | weekday: "0" |
| 72 | hour: "4" |
| 73 | minute: "30" |
| 74 |