(address . guix-patches@gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
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
---
doc/guix.texi | 36 +++++++++----
gnu/services/backup.scm | 114 ++++++++++++++++++++++++++++++++++------
2 files changed, 123 insertions(+), 27 deletions(-)
Toggle diff (257 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 57030102ca..f77b765933 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -41987,19 +41987,16 @@ Miscellaneous Services
"/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-job
@end example
@c %start of fragment
@@ -42030,6 +42027,22 @@ Miscellaneous Services
@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 the name of the job and be under @code{/var/log/restic-backup}.
+
+@item @code{max-duration} (type: maybe-number)
+The maximum duration in seconds that a job may last. Past
+@code{max-duration} seconds, the job will 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.
@@ -42042,9 +42055,12 @@ Miscellaneous Services
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..fc8934873b 100644
--- a/gnu/services/backup.scm
+++ b/gnu/services/backup.scm
@@ -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 @@ (define-module (gnu services backup)
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 (lowerable? value)
(define list-of-lowerables?
(list-of lowerable?))
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe string)
+(define-maybe number)
+
(define-configuration/no-serialization restic-backup-job
(restic
(package restic)
@@ -71,6 +83,22 @@ (define-configuration/no-serialization restic-backup-job
(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 the name of the job and be under @code{/var/log/restic-backup}.")
+ (max-duration
+ (maybe-number)
+ "The maximum duration in seconds that a job may last. Past
+@code{max-duration} seconds, the job will 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 +112,12 @@ (define-configuration/no-serialization restic-backup-job
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 +206,56 @@ (define (restic-guix jobs)
(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
+ (string-append name "-job"))))
+ (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
+ (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 +283,24 @@ (define restic-backup-service-profile
(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 +312,5 @@ (define restic-backup-service-type
jobs)))))
(default-value (restic-backup-configuration))
(description
- "This service configures @code{mcron} jobs for running backups
+ "This service configures @code{Shepherd} timers for running backups
with @code{restic}.")))
base-commit: 2743faebb2893f65fb29a5cfd55c72a66a2b98a9
--
2.46.0