diff options
| author | Robert Günzler <r@gnzler.io> | 2025-09-07 17:32:13 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2025-09-07 17:42:42 +0200 |
| commit | 38be6c13f76e893cf47636257071b440dec0c5b2 (patch) | |
| tree | f7ded3235a7e3362cbd1ed3d91523968fe0faf90 /bundles/restic-backup | |
initial commit
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bundles/restic-backup')
| -rw-r--r-- | bundles/restic-backup/files/cron_job | 5 | ||||
| -rw-r--r-- | bundles/restic-backup/files/excludes | 3 | ||||
| -rw-r--r-- | bundles/restic-backup/files/includes | 3 | ||||
| -rw-r--r-- | bundles/restic-backup/files/restic.conf | 4 | ||||
| -rw-r--r-- | bundles/restic-backup/files/resticbackup | 36 | ||||
| -rw-r--r-- | bundles/restic-backup/files/resticrestore | 29 | ||||
| -rw-r--r-- | bundles/restic-backup/files/resticw | 21 | ||||
| -rw-r--r-- | bundles/restic-backup/items.py | 62 | ||||
| -rw-r--r-- | bundles/restic-backup/metadata.py | 7 |
9 files changed, 170 insertions, 0 deletions
diff --git a/bundles/restic-backup/files/cron_job b/bundles/restic-backup/files/cron_job new file mode 100644 index 0000000..6fcea06 --- /dev/null +++ b/bundles/restic-backup/files/cron_job @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu +/usr/bin/resticbackup >/dev/null 2>&1 +tgnotify '✨*${node.name}/resticbackup*\nsuccess!' >/dev/null 2>&1 +exit 0 diff --git a/bundles/restic-backup/files/excludes b/bundles/restic-backup/files/excludes new file mode 100644 index 0000000..8a02379 --- /dev/null +++ b/bundles/restic-backup/files/excludes @@ -0,0 +1,3 @@ +% for path in paths: +${path} +% endfor diff --git a/bundles/restic-backup/files/includes b/bundles/restic-backup/files/includes new file mode 100644 index 0000000..8a02379 --- /dev/null +++ b/bundles/restic-backup/files/includes @@ -0,0 +1,3 @@ +% for path in paths: +${path} +% endfor diff --git a/bundles/restic-backup/files/restic.conf b/bundles/restic-backup/files/restic.conf new file mode 100644 index 0000000..3807fd7 --- /dev/null +++ b/bundles/restic-backup/files/restic.conf @@ -0,0 +1,4 @@ +RESTIC_REPOSITORY=b2:${repo.vault.cmd(f"rbw get {node.name}/b2/backupbucket")}:/ +RESTIC_PASSWORD=${repo.vault.password_for(f"b2_bucket_{node.name}_pw")} +B2_ACCOUNT_ID=${repo.vault.cmd(f"rbw get {node.name}/b2/keyid")} +B2_ACCOUNT_KEY=${repo.vault.cmd(f"rbw get {node.name}/b2/appkey")} diff --git a/bundles/restic-backup/files/resticbackup b/bundles/restic-backup/files/resticbackup new file mode 100644 index 0000000..af61d7e --- /dev/null +++ b/bundles/restic-backup/files/resticbackup @@ -0,0 +1,36 @@ +#!/bin/sh +set -eu + +backup_includes=/etc/restic/includes +backup_excludes=/etc/restic/excludes +backup_tag=foobar +backup_keep=1m + +main() { + # remove stale locks + resticw unlock + + # run backup + resticw backup \ + --one-file-system \ + --files-from "${backup_includes}" \ + --exclude-file "${backup_excludes}" \ + --tag "${backup_tag}" + + # prune backups older than $backup_keep duration + resticw forget \ + --keep-within "${backup_keep}" \ + --prune \ + --group-by "paths,tags" \ + --tag "${backup_tag}" + + # TODO: restic check + # TODO: dump stats to file in metrics format to /run/prom/restic +} + +exit_hook() { + resticw unlock +} +trap exit_hook INT EXIT + +main diff --git a/bundles/restic-backup/files/resticrestore b/bundles/restic-backup/files/resticrestore new file mode 100644 index 0000000..68a10f1 --- /dev/null +++ b/bundles/restic-backup/files/resticrestore @@ -0,0 +1,29 @@ +#!/bin/sh +set -eu + +backup_tag=foobar +backup_restore_snapshot=latest + +main() { + # remove stale locks + resticw unlock + + # run restore + resticw restore \ + --verify \ + --tag "${backup_tag}" \ + --include "${1:-*}" \ + --target / \ + "${backup_restore_snapshot}" + + # TODO: restic check + # TODO: dump stats to file in metrics format to /run/prom/restic +} + +exit_hook() { + resticw unlock +} +trap exit_hook INT EXIT + +[ "$1" = "-h" ] && printf 'usage: resticrestore [TARGET]\n\n' && exit 0 +main "$1" diff --git a/bundles/restic-backup/files/resticw b/bundles/restic-backup/files/resticw new file mode 100644 index 0000000..e3a3e87 --- /dev/null +++ b/bundles/restic-backup/files/resticw @@ -0,0 +1,21 @@ +#!/bin/sh +set -eu + +# import environment from config +# can overwrite above defaults +set -a +. /etc/restic/restic.conf +set +a + +test -n "${RESTIC_REPOSITORY+1}" +test -n "${RESTIC_PASSWORD+1}" + +case "${RESTIC_REPOSITORY}" in +b2*) + test -n "${B2_ACCOUNT_ID+1}" + test -n "${B2_ACCOUNT_KEY+1}" + ;; + +esac + +exec restic $@ diff --git a/bundles/restic-backup/items.py b/bundles/restic-backup/items.py new file mode 100644 index 0000000..9ffc565 --- /dev/null +++ b/bundles/restic-backup/items.py @@ -0,0 +1,62 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +files = { + "/usr/bin/resticw": { + "source": "resticw", + "content_type": "text", + "mode": "0755", + }, + "/usr/bin/resticbackup": { + "source": "resticbackup", + "content_type": "text", + "mode": "0755", + }, + "/usr/bin/resticrestore": { + "source": "resticrestore", + "content_type": "text", + "mode": "0755", + }, + "/etc/restic/restic.conf": { + "source": "restic.conf", + "content_type": "mako", + "mode": "0600", + }, + "/etc/restic/includes": { + "source": "includes", + "content_type": "mako", + "context": { + "paths": sorted(node.metadata.get("backup/includes", {})), + }, + }, + "/etc/restic/excludes": { + "source": "includes", + "content_type": "mako", + "context": { + "paths": sorted(node.metadata.get("backup/excludes", {})), + }, + }, + "/etc/periodic/weekly/restic-backup": { + "source": "cron_job", + "content_type": "mako", + "mode": "0755", + "needs": { + "bundle:tgnotify", + } + }, +} + +actions = { + f"initialize_restic_repository": { + "command": "resticw init", + "unless": "resticw cat config", + "needs": { + "file:/usr/bin/resticw", + "file:/etc/restic/restic.conf", + }, + "triggered": True, + "triggered_by": { + "file:/etc/restic/restic.conf", + }, + } +} diff --git a/bundles/restic-backup/metadata.py b/bundles/restic-backup/metadata.py new file mode 100644 index 0000000..583be1f --- /dev/null +++ b/bundles/restic-backup/metadata.py @@ -0,0 +1,7 @@ +defaults = { + "apk": { + "packages": { + "restic": {}, + }, + }, +} |