backup stuff
This commit is contained in:
parent
01b83a80ec
commit
27e036eccd
16 changed files with 513 additions and 0 deletions
10
ansible/roles/backup_store/templates/pull-backups.service.j2
Normal file
10
ansible/roles/backup_store/templates/pull-backups.service.j2
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[Unit]
|
||||
Description=Pull encrypted backups from production
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User={{ ansible_user_id }}
|
||||
ExecStart=/usr/local/bin/pull-backups.sh
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=pull-backups
|
||||
43
ansible/roles/backup_store/templates/pull-backups.sh.j2
Normal file
43
ansible/roles/backup_store/templates/pull-backups.sh.j2
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
# Pull encrypted backups from production — managed by Ansible (roles/backup_store)
|
||||
#
|
||||
# Everything here is already ciphertext: this host only moves and expires files,
|
||||
# and holds no key that can read them.
|
||||
set -uo pipefail # deliberately NOT -e; see the loop below
|
||||
|
||||
SSH_KEY="{{ backup_store_ssh_key }}"
|
||||
STORE="{{ backup_store_dir }}"
|
||||
|
||||
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
||||
fail() { echo "$(date '+%Y-%m-%d %H:%M:%S') ERROR: $*" >&2; failures=$((failures + 1)); }
|
||||
|
||||
failures=0
|
||||
|
||||
# One source failing must not stop the others. The whole point of this box is
|
||||
# that a single dead host cannot silently take the rest of the backups with it —
|
||||
# which is exactly how the laptop-based jobs failed unnoticed for nine months.
|
||||
{% for src in backup_store_sources %}
|
||||
# --- {{ src.name }} ---
|
||||
pull_{{ src.name | replace('-', '_') }}() {
|
||||
local dir="${STORE}/{{ src.name }}"
|
||||
mkdir -p "$dir"
|
||||
log "Pulling {{ src.name }} from {{ src.source }}..."
|
||||
if rsync -az --timeout=120 \
|
||||
-e "ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15" \
|
||||
"{{ src.source }}" "$dir/"; then
|
||||
log " {{ src.name }}: ok ($(find "$dir" -maxdepth 1 -type f | wc -l) artefacts, $(du -sh "$dir" | cut -f1))"
|
||||
else
|
||||
fail "{{ src.name }}: rsync failed"
|
||||
return 1
|
||||
fi
|
||||
log " {{ src.name }}: pruning older than {{ src.retention_days }} days"
|
||||
find "$dir" -maxdepth 1 -type f -name '{{ src.name }}_*' -mtime +{{ src.retention_days }} -delete
|
||||
}
|
||||
pull_{{ src.name | replace('-', '_') }} || true
|
||||
|
||||
{% endfor %}
|
||||
if [ "$failures" -gt 0 ]; then
|
||||
log "FAILED: $failures source(s) did not pull"
|
||||
exit 1
|
||||
fi
|
||||
log "All sources pulled."
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=Daily offsite backup pull
|
||||
|
||||
[Timer]
|
||||
OnCalendar={{ backup_store_on_calendar }}
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Loading…
Add table
Add a link
Reference in a new issue