diff options
| author | Giacomo Leidi <goodoldpaul@autistici.org> | 2025-01-19 23:04:00 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2025-01-25 00:04:27 +0100 |
| commit | 35c6ae6e58f1cfd397602d12c4f4e70d37f0eb90 (patch) | |
| tree | fd6e79d4783af33baf9e78c6ae9b3a36df8961ec /gnu/services/backup.scm | |
| parent | 44d12f9663ca363134636588279ef70decd1d551 (diff) | |
services: restic-backup: Implement as a Shepherd timer.
This patch implements restic backup with Shepherd services. It is
supposed not to break any existing setup.
* gnu/services/backup.scm (restic-backup-job): Add Shepherd
configuration options;
(restic-backup-job->mcron-job): Replace with...;
(restic-job-log-file): New procedure;
(restic-backup-job->shepherd-service): New procedure;
(restic-backup-activation): New procedure;
(restic-backup-service-type): Replace mcron with Shepherd extension and add
activation extension hook.
* doc/guix.texi: Document it.
Change-Id: I66de3b6a1cb6177f9e4ee0c2acf3013ecbcdd338
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Diffstat (limited to 'gnu/services/backup.scm')
| -rw-r--r-- | gnu/services/backup.scm | 122 |
1 files changed, 103 insertions, 19 deletions
diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm index 555e9fc9590..99a79ff5fbe 100644 --- a/gnu/services/backup.scm +++ b/gnu/services/backup.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org> | 2 | ;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org> |
| 3 | ;;; | 3 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 4 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 5 | ;;; |
| @@ -18,9 +18,10 @@ | |||
| 18 | 18 | ||
| 19 | (define-module (gnu services backup) | 19 | (define-module (gnu services backup) |
| 20 | #:use-module (gnu packages backup) | 20 | #:use-module (gnu packages backup) |
| 21 | #:use-module (gnu packages bash) | ||
| 21 | #:use-module (gnu services) | 22 | #:use-module (gnu services) |
| 22 | #:use-module (gnu services configuration) | 23 | #:use-module (gnu services configuration) |
| 23 | #:use-module (gnu services mcron) | 24 | #:use-module (gnu services shepherd) |
| 24 | #:use-module (guix build-system copy) | 25 | #:use-module (guix build-system copy) |
| 25 | #:use-module (guix gexp) | 26 | #:use-module (guix gexp) |
| 26 | #:use-module ((guix licenses) | 27 | #:use-module ((guix licenses) |
| @@ -33,11 +34,16 @@ | |||
| 33 | restic-backup-job-fields | 34 | restic-backup-job-fields |
| 34 | restic-backup-job-restic | 35 | restic-backup-job-restic |
| 35 | restic-backup-job-user | 36 | restic-backup-job-user |
| 37 | restic-backup-job-group | ||
| 38 | restic-backup-job-log-file | ||
| 39 | restic-backup-job-max-duration | ||
| 40 | restic-backup-job-wait-for-termination? | ||
| 36 | restic-backup-job-name | 41 | restic-backup-job-name |
| 37 | restic-backup-job-repository | 42 | restic-backup-job-repository |
| 38 | restic-backup-job-password-file | 43 | restic-backup-job-password-file |
| 39 | restic-backup-job-schedule | 44 | restic-backup-job-schedule |
| 40 | restic-backup-job-files | 45 | restic-backup-job-files |
| 46 | restic-backup-job-requirement | ||
| 41 | restic-backup-job-verbose? | 47 | restic-backup-job-verbose? |
| 42 | restic-backup-job-extra-flags | 48 | restic-backup-job-extra-flags |
| 43 | 49 | ||
| @@ -64,6 +70,12 @@ | |||
| 64 | (define list-of-lowerables? | 70 | (define list-of-lowerables? |
| 65 | (list-of lowerable?)) | 71 | (list-of lowerable?)) |
| 66 | 72 | ||
| 73 | (define list-of-symbols? | ||
| 74 | (list-of symbol?)) | ||
| 75 | |||
| 76 | (define-maybe/no-serialization string) | ||
| 77 | (define-maybe/no-serialization number) | ||
| 78 | |||
| 67 | (define-configuration/no-serialization restic-backup-job | 79 | (define-configuration/no-serialization restic-backup-job |
| 68 | (restic | 80 | (restic |
| 69 | (package restic) | 81 | (package restic) |
| @@ -71,6 +83,23 @@ | |||
| 71 | (user | 83 | (user |
| 72 | (string "root") | 84 | (string "root") |
| 73 | "The user used for running the current job.") | 85 | "The user used for running the current job.") |
| 86 | (group | ||
| 87 | (string "root") | ||
| 88 | "The group used for running the current job.") | ||
| 89 | (log-file | ||
| 90 | (maybe-string) | ||
| 91 | "The file system path to the log file for this job. By default the file will | ||
| 92 | have be @file{/var/log/restic-backup/@var{job-name}.log}, where @var{job-name} is the | ||
| 93 | name defined in the @code{name} field.") | ||
| 94 | (max-duration | ||
| 95 | (maybe-number) | ||
| 96 | "The maximum duration in seconds that a job may last. Past | ||
| 97 | @code{max-duration} seconds, the job is forcefully terminated.") | ||
| 98 | (wait-for-termination? | ||
| 99 | (boolean #f) | ||
| 100 | "Wait until the job has finished before considering executing it again; | ||
| 101 | otherwise, perform it strictly on every occurrence of event, at the risk of | ||
| 102 | having multiple instances running concurrently.") | ||
| 74 | (name | 103 | (name |
| 75 | (string) | 104 | (string) |
| 76 | "A string denoting a name for this job.") | 105 | "A string denoting a name for this job.") |
| @@ -84,9 +113,12 @@ will be used to set the @code{RESTIC_PASSWORD} environment variable for the | |||
| 84 | current job.") | 113 | current job.") |
| 85 | (schedule | 114 | (schedule |
| 86 | (gexp-or-string) | 115 | (gexp-or-string) |
| 87 | "A string or a gexp that will be passed as time specification in the mcron | 116 | "A string or a gexp representing the frequency of the backup. Gexp must |
| 88 | job specification (@pxref{Syntax, mcron job specifications,, mcron, | 117 | evaluate to @code{calendar-event} records or to strings. Strings must contain |
| 89 | GNU@tie{}mcron}).") | 118 | Vixie cron date lines.") |
| 119 | (requirement | ||
| 120 | (list-of-symbols '()) | ||
| 121 | "The list of Shepherd services that this backup job depends upon.") | ||
| 90 | (files | 122 | (files |
| 91 | (list-of-lowerables '()) | 123 | (list-of-lowerables '()) |
| 92 | "The list of files or directories to be backed up. It must be a list of | 124 | "The list of files or directories to be backed up. It must be a list of |
| @@ -175,16 +207,59 @@ command-line arguments to the current job @command{restic backup} invokation.")) | |||
| 175 | 207 | ||
| 176 | (main (command-line))))) | 208 | (main (command-line))))) |
| 177 | 209 | ||
| 178 | (define (restic-backup-job->mcron-job config) | 210 | (define (restic-job-log-file job) |
| 179 | (let ((user | 211 | (let ((name (restic-backup-job-name job)) |
| 180 | (restic-backup-job-user config)) | 212 | (log-file (restic-backup-job-log-file job))) |
| 181 | (schedule | 213 | (if (maybe-value-set? log-file) |
| 182 | (restic-backup-job-schedule config)) | 214 | log-file |
| 183 | (name | 215 | (string-append "/var/log/restic-backup/" name ".log")))) |
| 184 | (restic-backup-job-name config))) | 216 | |
| 185 | #~(job #$schedule | 217 | (define (restic-backup-job->shepherd-service config) |
| 186 | #$(string-append "restic-guix backup " name) | 218 | (let ((schedule (restic-backup-job-schedule config)) |
| 187 | #:user #$user))) | 219 | (name (restic-backup-job-name config)) |
| 220 | (user (restic-backup-job-user config)) | ||
| 221 | (group (restic-backup-job-group config)) | ||
| 222 | (max-duration (restic-backup-job-max-duration config)) | ||
| 223 | (wait-for-termination? (restic-backup-job-wait-for-termination? config)) | ||
| 224 | (log-file (restic-job-log-file config)) | ||
| 225 | (requirement (restic-backup-job-requirement config))) | ||
| 226 | (shepherd-service (provision `(,(string->symbol name))) | ||
| 227 | (requirement | ||
| 228 | `(user-processes file-systems ,@requirement)) | ||
| 229 | (documentation | ||
| 230 | "Run @code{restic} backed backups on a regular basis.") | ||
| 231 | (modules '((shepherd service timer))) | ||
| 232 | (start | ||
| 233 | #~(make-timer-constructor | ||
| 234 | (if (string? #$schedule) | ||
| 235 | (cron-string->calendar-event #$schedule) | ||
| 236 | #$schedule) | ||
| 237 | (command | ||
| 238 | (list | ||
| 239 | ;; We go through bash, instead of executing | ||
| 240 | ;; restic-guix directly, because the login shell | ||
| 241 | ;; gives us the correct user environment that some | ||
| 242 | ;; backends require, such as rclone. | ||
| 243 | (string-append #+bash-minimal "/bin/bash") | ||
| 244 | "-l" "-c" | ||
| 245 | (string-append "restic-guix backup " #$name)) | ||
| 246 | #:user #$user | ||
| 247 | #:group #$group | ||
| 248 | #:environment-variables | ||
| 249 | (list | ||
| 250 | (string-append | ||
| 251 | "HOME=" (passwd:dir (getpwnam #$user))))) | ||
| 252 | #:log-file #$log-file | ||
| 253 | #:wait-for-termination? #$wait-for-termination? | ||
| 254 | #:max-duration #$(and (maybe-value-set? max-duration) | ||
| 255 | max-duration))) | ||
| 256 | (stop | ||
| 257 | #~(make-timer-destructor)) | ||
| 258 | (actions (list (shepherd-action | ||
| 259 | (name 'trigger) | ||
| 260 | (documentation "Manually trigger a backup, | ||
| 261 | without waiting for the scheduled time.") | ||
| 262 | (procedure #~trigger-timer))))))) | ||
| 188 | 263 | ||
| 189 | (define (restic-guix-wrapper-package jobs) | 264 | (define (restic-guix-wrapper-package jobs) |
| 190 | (package | 265 | (package |
| @@ -212,15 +287,24 @@ without waiting for the scheduled job to run.") | |||
| 212 | (restic-guix-wrapper-package jobs)) | 287 | (restic-guix-wrapper-package jobs)) |
| 213 | '()))) | 288 | '()))) |
| 214 | 289 | ||
| 290 | (define (restic-backup-activation config) | ||
| 291 | #~(for-each | ||
| 292 | (lambda (log-file) | ||
| 293 | (mkdir-p (dirname log-file))) | ||
| 294 | (list #$@(map restic-job-log-file | ||
| 295 | (restic-backup-configuration-jobs config))))) | ||
| 296 | |||
| 215 | (define restic-backup-service-type | 297 | (define restic-backup-service-type |
| 216 | (service-type (name 'restic-backup) | 298 | (service-type (name 'restic-backup) |
| 217 | (extensions | 299 | (extensions |
| 218 | (list | 300 | (list |
| 301 | (service-extension activation-service-type | ||
| 302 | restic-backup-activation) | ||
| 219 | (service-extension profile-service-type | 303 | (service-extension profile-service-type |
| 220 | restic-backup-service-profile) | 304 | restic-backup-service-profile) |
| 221 | (service-extension mcron-service-type | 305 | (service-extension shepherd-root-service-type |
| 222 | (lambda (config) | 306 | (lambda (config) |
| 223 | (map restic-backup-job->mcron-job | 307 | (map restic-backup-job->shepherd-service |
| 224 | (restic-backup-configuration-jobs | 308 | (restic-backup-configuration-jobs |
| 225 | config)))))) | 309 | config)))))) |
| 226 | (compose concatenate) | 310 | (compose concatenate) |
| @@ -232,5 +316,5 @@ without waiting for the scheduled job to run.") | |||
| 232 | jobs))))) | 316 | jobs))))) |
| 233 | (default-value (restic-backup-configuration)) | 317 | (default-value (restic-backup-configuration)) |
| 234 | (description | 318 | (description |
| 235 | "This service configures @code{mcron} jobs for running backups | 319 | "This service configures Shepherd timers for running backups |
| 236 | with @code{restic}."))) | 320 | with restic."))) |
