diff options
author | Giacomo Leidi <[email protected]> | 2025-01-19 23:04:00 +0100 |
---|---|---|
committer | Ludovic Courtès <[email protected]> | 2025-01-25 00:04:27 +0100 |
commit | 35c6ae6e58f1cfd397602d12c4f4e70d37f0eb90 (patch) | |
tree | fd6e79d4783af33baf9e78c6ae9b3a36df8961ec | |
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 <[email protected]>
-rw-r--r-- | doc/guix.texi | 39 | ||||
-rw-r--r-- | gnu/services/backup.scm | 122 |
2 files changed, 131 insertions, 30 deletions
diff --git a/doc/guix.texi b/doc/guix.texi index b1463387f3..9a53bdcd37 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -111,7 +111,7 @@ Copyright @copyright{} 2022 (@* Copyright @copyright{} 2022 John Kehayias@* Copyright @copyright{} 2022–2023 Bruno Victal@* Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@* -Copyright @copyright{} 2023-2024 Giacomo Leidi@* +Copyright @copyright{} 2023-2025 Giacomo Leidi@* Copyright @copyright{} 2022 Antero Mejr@* Copyright @copyright{} 2023 Karl Hallsby@* Copyright @copyright{} 2023 Nathaniel Nicandro@* @@ -42710,19 +42710,16 @@ following configuration: "/etc/guix/signing-key.sec")))))))))) @end lisp -Each @code{restic-backup-job} translates to an mcron job which sets the +Each @code{restic-backup-job} translates to a Shepherd timer which sets the @env{RESTIC_PASSWORD} environment variable by reading the first line of @code{password-file} and runs @command{restic backup}, creating backups using rclone of all the files listed in the @code{files} field. -The @code{restic-backup-service-type} installs as well @code{restic-guix} -to the system profile, a @code{restic} utility wrapper that allows for easier -interaction with the Guix configured backup jobs. For example the following -could be used to instantaneusly trigger a backup for the above shown -configuration, without waiting for the scheduled job: +The @code{restic-backup-service-type} provides the ability to instantaneously +trigger a backup with the @code{trigger} Shepherd action: @example -restic-guix backup remote-ftp +sudo herd trigger remote-ftp @end example @c %start of fragment @@ -42753,6 +42750,23 @@ The restic package to be used for the current job. @item @code{user} (default: @code{"root"}) (type: string) The user used for running the current job. +@item @code{group} (default: @code{"root"}) (type: string) +The group used for running the current job. + +@item @code{log-file} (type: maybe-string) +The file system path to the log file for this job. By default the file will +have be @file{/var/log/restic-backup/@var{job-name}.log}, where @var{job-name} is the +name defined in the @code{name} field. + +@item @code{max-duration} (type: maybe-number) +The maximum duration in seconds that a job may last. Past +@code{max-duration} seconds, the job is forcefully terminated. + +@item @code{wait-for-termination?} (default: @code{#f}) (type: boolean) +Wait until the job has finished before considering executing it again; +otherwise, perform it strictly on every occurrence of event, at the risk of +having multiple instances running concurrently. + @item @code{repository} (type: string) The restic repository target of this job. @@ -42765,9 +42779,12 @@ that will be used to set the @env{RESTIC_PASSWORD} environment variable for the current job. @item @code{schedule} (type: gexp-or-string) -A string or a gexp that will be passed as time specification in the -mcron job specification (@pxref{Syntax, mcron job specifications,, -mcron,GNU@tie{}mcron}). +A string or a gexp representing the frequency of the backup. Gexp must +evaluate to @code{calendar-event} records or to strings. Strings must contain +Vixie cron date lines. + +@item @code{requirement} (default: @code{'()}) (type: list-of-symbols) +The list of Shepherd services that this backup job depends upon. @item @code{files} (default: @code{'()}) (type: list-of-lowerables) The list of files or directories to be backed up. It must be a list of diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm index 555e9fc959..99a79ff5fb 100644 --- a/gnu/services/backup.scm +++ b/gnu/services/backup.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2024 Giacomo Leidi <[email protected]> +;;; Copyright © 2024, 2025 Giacomo Leidi <[email protected]> ;;; ;;; This file is part of GNU Guix. ;;; @@ -18,9 +18,10 @@ (define-module (gnu services backup) #:use-module (gnu packages backup) + #:use-module (gnu packages bash) #:use-module (gnu services) #:use-module (gnu services configuration) - #:use-module (gnu services mcron) + #:use-module (gnu services shepherd) #:use-module (guix build-system copy) #:use-module (guix gexp) #:use-module ((guix licenses) @@ -33,11 +34,16 @@ restic-backup-job-fields restic-backup-job-restic restic-backup-job-user + restic-backup-job-group + restic-backup-job-log-file + restic-backup-job-max-duration + restic-backup-job-wait-for-termination? restic-backup-job-name restic-backup-job-repository restic-backup-job-password-file restic-backup-job-schedule restic-backup-job-files + restic-backup-job-requirement restic-backup-job-verbose? restic-backup-job-extra-flags @@ -64,6 +70,12 @@ (define list-of-lowerables? (list-of lowerable?)) +(define list-of-symbols? + (list-of symbol?)) + +(define-maybe/no-serialization string) +(define-maybe/no-serialization number) + (define-configuration/no-serialization restic-backup-job (restic (package restic) @@ -71,6 +83,23 @@ (user (string "root") "The user used for running the current job.") + (group + (string "root") + "The group used for running the current job.") + (log-file + (maybe-string) + "The file system path to the log file for this job. By default the file will +have be @file{/var/log/restic-backup/@var{job-name}.log}, where @var{job-name} is the +name defined in the @code{name} field.") + (max-duration + (maybe-number) + "The maximum duration in seconds that a job may last. Past +@code{max-duration} seconds, the job is forcefully terminated.") + (wait-for-termination? + (boolean #f) + "Wait until the job has finished before considering executing it again; +otherwise, perform it strictly on every occurrence of event, at the risk of +having multiple instances running concurrently.") (name (string) "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 current job.") (schedule (gexp-or-string) - "A string or a gexp that will be passed as time specification in the mcron -job specification (@pxref{Syntax, mcron job specifications,, mcron, -GNU@tie{}mcron}).") + "A string or a gexp representing the frequency of the backup. Gexp must +evaluate to @code{calendar-event} records or to strings. Strings must contain +Vixie cron date lines.") + (requirement + (list-of-symbols '()) + "The list of Shepherd services that this backup job depends upon.") (files (list-of-lowerables '()) "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.")) (main (command-line))))) -(define (restic-backup-job->mcron-job config) - (let ((user - (restic-backup-job-user config)) - (schedule - (restic-backup-job-schedule config)) - (name - (restic-backup-job-name config))) - #~(job #$schedule - #$(string-append "restic-guix backup " name) - #:user #$user))) +(define (restic-job-log-file job) + (let ((name (restic-backup-job-name job)) + (log-file (restic-backup-job-log-file job))) + (if (maybe-value-set? log-file) + log-file + (string-append "/var/log/restic-backup/" name ".log")))) + +(define (restic-backup-job->shepherd-service config) + (let ((schedule (restic-backup-job-schedule config)) + (name (restic-backup-job-name config)) + (user (restic-backup-job-user config)) + (group (restic-backup-job-group config)) + (max-duration (restic-backup-job-max-duration config)) + (wait-for-termination? (restic-backup-job-wait-for-termination? config)) + (log-file (restic-job-log-file config)) + (requirement (restic-backup-job-requirement config))) + (shepherd-service (provision `(,(string->symbol name))) + (requirement + `(user-processes file-systems ,@requirement)) + (documentation + "Run @code{restic} backed backups on a regular basis.") + (modules '((shepherd service timer))) + (start + #~(make-timer-constructor + (if (string? #$schedule) + (cron-string->calendar-event #$schedule) + #$schedule) + (command + (list + ;; We go through bash, instead of executing + ;; restic-guix directly, because the login shell + ;; gives us the correct user environment that some + ;; backends require, such as rclone. + (string-append #+bash-minimal "/bin/bash") + "-l" "-c" + (string-append "restic-guix backup " #$name)) + #:user #$user + #:group #$group + #:environment-variables + (list + (string-append + "HOME=" (passwd:dir (getpwnam #$user))))) + #:log-file #$log-file + #:wait-for-termination? #$wait-for-termination? + #:max-duration #$(and (maybe-value-set? max-duration) + max-duration))) + (stop + #~(make-timer-destructor)) + (actions (list (shepherd-action + (name 'trigger) + (documentation "Manually trigger a backup, +without waiting for the scheduled time.") + (procedure #~trigger-timer))))))) (define (restic-guix-wrapper-package jobs) (package @@ -212,15 +287,24 @@ without waiting for the scheduled job to run.") (restic-guix-wrapper-package jobs)) '()))) +(define (restic-backup-activation config) + #~(for-each + (lambda (log-file) + (mkdir-p (dirname log-file))) + (list #$@(map restic-job-log-file + (restic-backup-configuration-jobs config))))) + (define restic-backup-service-type (service-type (name 'restic-backup) (extensions (list + (service-extension activation-service-type + restic-backup-activation) (service-extension profile-service-type restic-backup-service-profile) - (service-extension mcron-service-type + (service-extension shepherd-root-service-type (lambda (config) - (map restic-backup-job->mcron-job + (map restic-backup-job->shepherd-service (restic-backup-configuration-jobs config)))))) (compose concatenate) @@ -232,5 +316,5 @@ without waiting for the scheduled job to run.") jobs))))) (default-value (restic-backup-configuration)) (description - "This service configures @code{mcron} jobs for running backups -with @code{restic}."))) + "This service configures Shepherd timers for running backups +with restic."))) |