summaryrefslogtreecommitdiff
path: root/gnu/services
diff options
context:
space:
mode:
authorGiacomo Leidi <[email protected]>2025-01-19 23:04:00 +0100
committerLudovic Courtès <[email protected]>2025-01-25 00:04:27 +0100
commit35c6ae6e58f1cfd397602d12c4f4e70d37f0eb90 (patch)
treefd6e79d4783af33baf9e78c6ae9b3a36df8961ec /gnu/services
parent44d12f9663ca363134636588279ef70decd1d551 (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]>
Diffstat (limited to 'gnu/services')
-rw-r--r--gnu/services/backup.scm122
1 files changed, 103 insertions, 19 deletions
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.")))