Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CI

on:
pull_request:
push:
branches:
- main

jobs:
python:
name: Lint Python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install black
run: pip install ruff
- name: Check style
run: ruff format --quiet --line-length 120 --diff */*.py
- name: Lint
run: ruff check */*.py
41 changes: 31 additions & 10 deletions pyinfra_borgbackup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import random
from io import StringIO

from pyinfra.operations import apt, files, server
from pyinfra.operations import apt, files, server, systemd


def deploy_borgbackup(
Expand Down Expand Up @@ -56,9 +56,7 @@ def deploy_borgbackup(
if borg_repo.startswith("hetzner-backup:"):
files.put(
name="create SSH config",
src=importlib.resources.files(__package__)
.joinpath("dot_ssh", "config")
.open("rb"),
src=importlib.resources.files(__package__).joinpath("dot_ssh", "config").open("rb"),
dest="/root/.ssh/config",
user="root",
group="root",
Expand Down Expand Up @@ -90,13 +88,36 @@ def deploy_borgbackup(
],
)

files.template(
name="Create cron job for backup script",
src=importlib.resources.files(__package__).joinpath("borgbackup.cron"),
dest="/etc/cron.d/borgbackup",
user="root",
group="root",
files.file(
name="Remove old backup cronjob, replaced by systemd timer",
path="/etc/cron.d/borgbackup",
present=False,
)

backup_service_file = files.put(
src=importlib.resources.files(__package__).joinpath("borgbackup.service"),
dest="/etc/systemd/system/borgbackup.service",
mode="644",
)
systemd.service(
name="Setup borgbackup service",
service="borgbackup.service",
running=False,
enabled=False,
daemon_reload=backup_service_file.changed,
)

backup_timer_file = files.template(
src=importlib.resources.files(__package__).joinpath("borgbackup.timer.j2"),
dest="/etc/systemd/system/borgbackup.timer",
mode="644",
minute=str(random.randint(0, 59)),
hour=str(random.randint(0, 4)),
)
systemd.service(
name="Setup borgbackup timer",
service="borgbackup.timer",
running=True,
enabled=True,
daemon_reload=backup_timer_file.changed,
)
12 changes: 8 additions & 4 deletions pyinfra_borgbackup/backup-pre.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import argparse
import os


def cmd(command: str) -> int:
"""Run a command and return its exit code."""
return os.waitstatus_to_exitcode(os.system(command))


services = {
"isolationbot": "isolationbot.service",
"root": "docker.service",
Expand All @@ -19,10 +25,8 @@

for user in services:
if user == "root":
returncode = os.system(f"systemctl {args.command} {services[user]}")
returncode = cmd(f"systemctl {args.command} {services[user]}")
else:
returncode = os.system(
f"su -l {user} -c 'systemctl --user {args.command} {services[user]}'"
)
returncode = cmd(f"su -l {user} -c 'systemctl --user {args.command} {services[user]}'")
if returncode != 0:
print(f"WARNING: Failed to {args.command} {services[user]} as {user} user")
54 changes: 18 additions & 36 deletions pyinfra_borgbackup/backup.sh.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

function start_services {
# start the services/containers which write data again
rv=$?
if [ -f /root/backup-pre.py ]; then python3 /root/backup-pre.py start; fi
exit $(( $rv + $? ))
}

trap "start_services" EXIT
Expand All @@ -21,65 +23,45 @@ echo "$DEST1" > /dev/null

export BORG_RSH='ssh -F /root/.ssh/config -o "StrictHostKeyChecking=no"'

# don't fail just because the pod is not running
set +e
function run_borg () {
set +e # don't fail if borg quits with exit code 1 (warning)
borg $@
borg_rc=$?
if [[ $borg_rc -gt 1 ]]; then
exit $borg_rc
fi
set -e
}

# check if variable exists: https://stackoverflow.com/a/11369388
if [ "$SKIP_CHECK" = "true" ]; then
echo "skipping borg check."
else
borg check ${DEST1} &
if [ -n "${DEST2-}" ]; then
borg check ${DEST2} &
fi
run_borg check ${DEST1}
fi
wait

# stop services/containers which write data
if [ -f /root/backup-pre.py ]; then python3 /root/backup-pre.py stop; fi

borg create --stats --compression lzma ${DEST1}::'backup{now:%Y-%m-%d-%H}' {{ borg_args }} \
--exclude *.cache/ \
--exclude /dev \
--exclude /proc \
--exclude /sys \
--exclude /var/run \
--exclude /run \
--exclude /lost+found \
--exclude /mnt \
--exclude /tmp \
--exclude /media \
--exclude /var/lib/lxcfs && FINISHED=true &
if [ -n "${DEST2-}" ]; then
# If there is a second backup destination, create a backup there.
# Note: not yet supported by pyinfra deploy.
borg create --stats --compression lzma ${DEST2}::'backup{now:%Y-%m-%d-%H}' {{ borg_args }} \
run_borg create --stats --compression lzma ${DEST1}::'backup{now:%Y-%m-%d-%H}' {{ borg_args }} \
--exclude *.cache/ \
--exclude /dev \
--exclude /proc \
--exclude /sys \
--exclude /var/run \
--exclude /var/log \
--exclude /run \
--exclude /lost+found \
--exclude /mnt \
--exclude /tmp \
--exclude /media \
--exclude /var/lib/lxcfs && FINISHED=true &
fi
wait
--exclude /var/lib/lxcfs

{% if prometheus_file is defined %}
if [ "$FINISHED" = "true" ]; then
echo "borgbackup_last_completed $(date +%s)" > {{ prometheus_file }}
fi
echo "borgbackup_last_completed $(date +%s)" > {{ prometheus_file }}
{%- endif %}
start_services

set -e
borg prune --keep-daily=7 --keep-weekly=4 ${DEST1}
borg compact ${DEST1}
if [ -n "${DEST2-}" ]; then
borg prune --keep-daily=7 --keep-weekly=4 ${DEST2}
borg compact ${DEST2}
fi
run_borg prune --keep-daily=7 --keep-weekly=4 ${DEST1}
run_borg compact ${DEST1}

4 changes: 0 additions & 4 deletions pyinfra_borgbackup/borgbackup.cron

This file was deleted.

9 changes: 9 additions & 0 deletions pyinfra_borgbackup/borgbackup.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
Description=Run nightly backups
After=network.target

[Service]
Type=oneshot
ExecStart=/root/backup.sh


8 changes: 8 additions & 0 deletions pyinfra_borgbackup/borgbackup.timer.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Unit]
Description=Run nightly backups

[Timer]
OnCalendar=*-*-* {{ hour }}:{{ minute }}:00

[Install]
WantedBy=timers.target