From e1ac041de9a4660739cda323ef867c5d1d69ae1a Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Sat, 20 May 2023 13:33:18 +0200 Subject: Add restic cleanup service --- tw/services/restic.scm | 64 +++++++++++++++++++++++++++++++++++++++++--------- tw/system/vin.scm | 10 ++++++++ 2 files changed, 63 insertions(+), 11 deletions(-) (limited to 'tw') diff --git a/tw/services/restic.scm b/tw/services/restic.scm index 18c501a9..b3ad79a2 100644 --- a/tw/services/restic.scm +++ b/tw/services/restic.scm @@ -13,6 +13,8 @@ #:use-module (srfi srfi-1) #:export (restic-server-service-type restic-server-configuration + restic-cleanup-service-type + restic-cleanup-repository home-restic-backup-service-type restic-backup-repository)) @@ -103,24 +105,64 @@ private restic repos.") ;; Restic cleanup cronjobs (define-configuration/no-serialization restic-cleanup-repository - (repository-path string "The directory path of the restic repository to clean up.") - (owner (string %restic-user) "The user that owns the REPOSITORY-PATH.") - (group (string %restic-group) "The group that owns the REPOSITORY-PATH.")) + (schedule gexp "An mcron schedule, specified as a gexp +(@pxref{G-Expressions}), to use for the cleanup job. String, list or lambda +syntax is fine (@pxref{Syntax, mcron job specifications,, mcron, +GNU@tie{}mcron}).") + (url string "The directory path (or URL) of the restic repository to clean up.") + (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.") + (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.") + (keep-weekly (maybe-integer %unset-value) "Keep the last N weekly snapshots.") + (keep-monthly (maybe-integer %unset-value) "Keep the last N monthly snapshots.") + (keep-yearly (maybe-integer %unset-value) "Keep the last N yearly snapshots.") + (keep-within (maybe-string %unset-value) "Keep snapshots newer than the +given duration relative to the latest snapshot.")) + +(define (restic-cleanup-cronjobs repositories) + (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)))) -(define-configuration/no-serialization restic-cleanup-configuration - (restic (package restic) "The restic package to use.")) + (define (cronjob repo) + #~(job #$(restic-cleanup-repository-schedule repo) + #$(program-file + ;; Make cron commands for different repos easier to distinguish. + (format #f "restic-cleanup-~a-command" + (string-trim-right (basename (restic-cleanup-repository-url repo)) #\/)) + #~(begin + ;; `setgid' first, while we're still root. + (setgid (group:gid (getgr #$%restic-group))) + (setuid (passwd:uid (getpw #$%restic-user))) + (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" + #$@(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)) + #$@(arg-with-value "--keep-daily" (restic-cleanup-repository-keep-daily repo)) + #$@(arg-with-value "--keep-weekly" (restic-cleanup-repository-keep-weekly repo)) + #$@(arg-with-value "--keep-monthly" (restic-cleanup-repository-keep-monthly repo)) + #$@(arg-with-value "--keep-yearly" (restic-cleanup-repository-keep-yearly repo))))))) -(define (restic-deletion-service config) #f) -(define (restic-prune-service config) #f) + (map cronjob repositories)) (define-public restic-cleanup-service-type (service-type (name 'restic-cleanup) (extensions - (list (service-extension mcron-service-type restic-deletion-service) - (service-extension mcron-service-type restic-prune-service))) - (description - ""))) + (list (service-extension mcron-service-type restic-cleanup-cronjobs))) + (compose concatenate) + (extend append) + (default-value '()) + (description "Clean up old restic snapshots on a schedule."))) ;; Restic scheduled backup jobs diff --git a/tw/system/vin.scm b/tw/system/vin.scm index 9c467a54..3076fd8f 100644 --- a/tw/system/vin.scm +++ b/tw/system/vin.scm @@ -53,6 +53,16 @@ (prometheus? #t) (prometheus-auth? #f))) + (service restic-cleanup-service-type + (list (restic-cleanup-repository + ;; Laptop backups run at "0 */2 * * *". + (schedule #~"10 5 * * *") ; daily at 05:10 + (url "/var/backups/restic/timo/laptop") + (password-file "/etc/restic/timo-laptop") + (keep-within "14d") + (keep-weekly 52) + (keep-monthly -1)))) + (server-base-services host-name))) ;; The list of user accounts ('root' is implicit). -- cgit v1.2.3