From ce80e63e1af034f5493d255fb03e2b16eeeb6b91 Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Sat, 20 May 2023 15:56:05 +0200 Subject: Clean up phone backups --- tw/services/restic.scm | 34 +++++++++++++++++++++++++++------- tw/system/vin.scm | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/tw/services/restic.scm b/tw/services/restic.scm index b3ad79a2..62764750 100644 --- a/tw/services/restic.scm +++ b/tw/services/restic.scm @@ -18,8 +18,8 @@ home-restic-backup-service-type restic-backup-repository)) -(define-public %restic-user "restic") -(define-public %restic-group "restic") +(define %restic-user "restic") +(define %restic-group "restic") ;; Restic REST server @@ -104,6 +104,8 @@ private restic repos.") ;; Restic cleanup cronjobs +(define-maybe list-of-strings) + (define-configuration/no-serialization restic-cleanup-repository (schedule gexp "An mcron schedule, specified as a gexp (@pxref{G-Expressions}), to use for the cleanup job. String, list or lambda @@ -113,6 +115,9 @@ GNU@tie{}mcron}).") (password-file string "The file name containing the repository password. Must be readable by the @code{%restic-user}.") (restic (package restic) "The restic package to use.") + (snapshot-paths (maybe-list-of-strings %unset-value) "Only consider +snapshots which include these paths.") + (prune? (boolean #t) "Immediately prune the repo after deleting snapshots.") (keep-last (maybe-integer %unset-value) "Keep the last N snapshots.") (keep-hourly (maybe-integer %unset-value) "Keep the last N hourly snapshots.") (keep-daily (maybe-integer %unset-value) "Keep the last N daily snapshots.") @@ -126,9 +131,22 @@ given duration relative to the latest snapshot.")) (define (arg-with-value arg value) "Produce a list for the given command-line argument with optional value. The result is inteded to be substituted into a `gexp' using `#$@', e.g. into -an `execl' call. Values are converted to strings as if by `display'." - (if (eq? %unset-value value) '() - (list arg (format #f "~a" value)))) +an `execl' call. Numbers are converted to strings as if by `display'. Lists +are turned into multiple arguments. For booleans, ARG is returned if VALUE is +true." + (define (to-string thing) + (format #f "~a" thing)) + (cond + ((eq? %unset-value value) + '()) + ((boolean? value) + (if value (list arg) '())) + ((list? value) + (append-map (lambda (item) + (list arg (to-string item))) + value)) + (else + (list arg (to-string value))))) (define (cronjob repo) #~(job #$(restic-cleanup-repository-schedule repo) @@ -143,7 +161,9 @@ an `execl' call. Values are converted to strings as if by `display'." (setenv "RESTIC_REPOSITORY" '#$(restic-cleanup-repository-url repo)) (setenv "RESTIC_PASSWORD_FILE" '#$(restic-cleanup-repository-password-file repo)) (execl #$(file-append (restic-cleanup-repository-restic repo) "/bin/restic") - "restic" "forget" "--no-cache" "--prune" + "restic" "forget" "--no-cache" + #$@(arg-with-value "--prune" (restic-cleanup-repository-prune? repo)) + #$@(arg-with-value "--path" (restic-cleanup-repository-snapshot-paths repo)) #$@(arg-with-value "--keep-within" (restic-cleanup-repository-keep-within repo)) #$@(arg-with-value "--keep-last" (restic-cleanup-repository-keep-last repo)) #$@(arg-with-value "--keep-hourly" (restic-cleanup-repository-keep-hourly repo)) @@ -154,7 +174,7 @@ an `execl' call. Values are converted to strings as if by `display'." (map cronjob repositories)) -(define-public restic-cleanup-service-type +(define restic-cleanup-service-type (service-type (name 'restic-cleanup) (extensions diff --git a/tw/system/vin.scm b/tw/system/vin.scm index 3076fd8f..41842b4e 100644 --- a/tw/system/vin.scm +++ b/tw/system/vin.scm @@ -56,11 +56,47 @@ (service restic-cleanup-service-type (list (restic-cleanup-repository ;; Laptop backups run at "0 */2 * * *". - (schedule #~"10 5 * * *") ; daily at 05:10 + (schedule #~"0 5 * * *") (url "/var/backups/restic/timo/laptop") (password-file "/etc/restic/timo-laptop") (keep-within "14d") (keep-weekly 52) + (keep-monthly -1)) + + ;; Phone backups run with a new version of restic, which creates + ;; v2 repos by default. Guix' older restic version can't read + ;; these, so create the repo on the server before pushing to it. + ;; Restic doesn't automatically upgrade the repo version. + ;; + ;; Phone backups run "hourly" (modulo Android's throttling of + ;; the Restic app), but the underlying data changes at most once + ;; a day, so use `keep-daily' instead of `keep-within'. + + (restic-cleanup-repository + (schedule #~"0 3 * * *") + (url "/var/backups/restic/timo/phone") + (password-file "/etc/restic/timo-phone") + (snapshot-paths '("/storage/FF37-F8E6/SignalBackup")) + ;; We only really care about the last signal backup, but guard + ;; against accidental deletion by keeping more. + (keep-daily 7)) + + (restic-cleanup-repository + (schedule #~"0 4 * * *") + (url "/var/backups/restic/timo/phone") + (password-file "/etc/restic/timo-phone") + (snapshot-paths '("/storage/emulated/0/Backups")) + (keep-daily 14) + (keep-monthly -1)) + + (restic-cleanup-repository + ;; OAndBackupX/NeoBackup backups can run until the early-ish + ;; morning; cleanup once they're all done and pushed. + (schedule #~"0 9 * * *") + (url "/var/backups/restic/timo/phone") + (password-file "/etc/restic/timo-phone") + (snapshot-paths '("/storage/FF37-F8E6/OAndBackupX")) + (keep-daily 14) (keep-monthly -1)))) (server-base-services host-name))) -- cgit v1.2.3