[PATCH] services: restic-backup: Implement as a Shepherd timer.

  • Open
  • quality assurance status badge
Details
2 participants
  • Giacomo Leidi
  • Ludovic Courtès
Owner
unassigned
Submitted by
Giacomo Leidi
Severity
normal
G
G
Giacomo Leidi wrote 47 hours ago
(address . guix-patches@gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
f52cde358b609d18f43bf62f1dfe63835c1a57b9.1734950765.git.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
P
P
paul wrote 46 hours ago
block 72803 by 75045
(address . control@debbugs.gnu.org)
581fb166-b3b6-4eab-a349-ff7a75798873@autistici.org
block 72803 by 75045
L
L
Ludovic Courtès wrote 23 hours ago
Re: [bug#75045] [PATCH] services: restic-backup: Implement as a Shepherd timer.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
87ikr95ppo.fsf@gnu.org
Hi Giacomo,

Giacomo Leidi <goodoldpaul@autistici.org> skribis:

Toggle quote (15 lines)
> 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

Woo, nice!

As mentioned in https://issues.guix.gnu.org/74860, I think we should
postpone a little bit since these new features won’t work for people who
haven’t rebooted into Shepherd 1.0, and they’ll get possibly confusing
messages when reconfiguring.

How we should postpone, I’m not sure. The conservative approach would
be to wait until after the next Guix release, but that doesn’t sound
reasonable… One month after the Shepherd upgrade, which would be
Jan. 9th? It’s common for servers to have much longer uptimes though,
plus there’s end-of-year vacation here.

Toggle quote (5 lines)
> + (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}.")

Rather @file, with the “.log” suffix too, if I’m not mistaken.

Toggle quote (5 lines)
> + (max-duration
> + (maybe-number)
> + "The maximum duration in seconds that a job may last. Past
> +@code{max-duration} seconds, the job will forcefully terminated.")

s/will/is/

Toggle quote (3 lines)
> + (shepherd-service (provision `(,(string->symbol
> + (string-append name "-job"))))

I would tend to not add the “-job” suffix, but that’s a matter of taste!

Toggle quote (6 lines)
> + (command
> + (list
> + (string-append #+bash-minimal "/bin/bash")
> + "-l" "-c"
> + (string-append "restic-guix backup " #$name))

Why go through bash? Would it be possible to execute ‘restic-guix’
directly?

Toggle quote (5 lines)
> (description
> - "This service configures @code{mcron} jobs for running backups
> + "This service configures @code{Shepherd} timers for running backups
> with @code{restic}.")))

You can drop @code here.
?
Your comment

Commenting via the web interface is currently disabled.

To comment on this conversation send an email to 75045@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 75045
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch