GNU bug report logs - #75045
[PATCH] services: restic-backup: Implement as a Shepherd timer.

Previous Next

Package: guix-patches;

Reported by: Giacomo Leidi <goodoldpaul <at> autistici.org>

Date: Mon, 23 Dec 2024 10:47:02 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 75045 in the body.
You can then email your comments to 75045 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to ludo <at> gnu.org, maxim.cournoyer <at> gmail.com, guix-patches <at> gnu.org:
bug#75045; Package guix-patches. (Mon, 23 Dec 2024 10:47:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Giacomo Leidi <goodoldpaul <at> autistici.org>:
New bug report received and forwarded. Copy sent to ludo <at> gnu.org, maxim.cournoyer <at> gmail.com, guix-patches <at> gnu.org. (Mon, 23 Dec 2024 10:47:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Giacomo Leidi <goodoldpaul <at> autistici.org>
To: guix-patches <at> gnu.org
Cc: Giacomo Leidi <goodoldpaul <at> autistici.org>
Subject: [PATCH] services: restic-backup: Implement as a Shepherd timer.
Date: Mon, 23 Dec 2024 11:46:05 +0100
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(-)

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 <at> 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 <at> 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





Added indication that bug 75045 blocks72803 Request was from paul <goodoldpaul <at> autistici.org> to control <at> debbugs.gnu.org. (Mon, 23 Dec 2024 11:27:02 GMT) Full text and rfc822 format available.

Information forwarded to guix-patches <at> gnu.org:
bug#75045; Package guix-patches. (Tue, 24 Dec 2024 10:43:02 GMT) Full text and rfc822 format available.

Message #10 received at 75045 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Giacomo Leidi <goodoldpaul <at> autistici.org>
Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>, 75045 <at> debbugs.gnu.org
Subject: Re: [bug#75045] [PATCH] services: restic-backup: Implement as a
 Shepherd timer.
Date: Tue, 24 Dec 2024 11:40:19 +0100
Hi Giacomo,

Giacomo Leidi <goodoldpaul <at> autistici.org> skribis:

> 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.

> +  (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.

> +  (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/

> +    (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!

> +                          (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?

>                  (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.





Information forwarded to guix-patches <at> gnu.org:
bug#75045; Package guix-patches. (Fri, 17 Jan 2025 20:29:02 GMT) Full text and rfc822 format available.

Message #13 received at 75045 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Giacomo Leidi <goodoldpaul <at> autistici.org>
Cc: 75045 <at> debbugs.gnu.org, Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Subject: Re: [bug#75045] [PATCH] services: restic-backup: Implement as a
 Shepherd timer.
Date: Fri, 17 Jan 2025 21:28:22 +0100
Hello,

Ludovic Courtès <ludo <at> gnu.org> skribis:

> 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.

Now that <https://issues.guix.gnu.org/74860> is merged, I think we can
gradually start migrating services such as this one.  Feel free to send v2!

Ludo’.




Information forwarded to guix-patches <at> gnu.org:
bug#75045; Package guix-patches. (Sun, 19 Jan 2025 22:03:01 GMT) Full text and rfc822 format available.

Message #16 received at 75045 <at> debbugs.gnu.org (full text, mbox):

From: paul <goodoldpaul <at> autistici.org>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>, 75045 <at> debbugs.gnu.org
Subject: Re: [bug#75045] [PATCH] services: restic-backup: Implement as a
 Shepherd timer.
Date: Sun, 19 Jan 2025 23:02:26 +0100
[Message part 1 (text/plain, inline)]
Hi Ludo',

I should have addressed your comments, thank you for your help! I'm 
about to send a v2.

On 12/24/24 11:40, Ludovic Courtès wrote:
>> +                          (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?
There may be some better way but 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.

I hope this clarifies and I'm definitely open to change this in case we 
find a better solution.

cheers,

giacomo
[Message part 2 (text/html, inline)]

Information forwarded to ludo <at> gnu.org, maxim.cournoyer <at> gmail.com, guix-patches <at> gnu.org:
bug#75045; Package guix-patches. (Sun, 19 Jan 2025 22:05:02 GMT) Full text and rfc822 format available.

Message #19 received at 75045 <at> debbugs.gnu.org (full text, mbox):

From: Giacomo Leidi <goodoldpaul <at> autistici.org>
To: 75045 <at> debbugs.gnu.org
Cc: Giacomo Leidi <goodoldpaul <at> autistici.org>
Subject: [PATCH v2] services: restic-backup: Implement as a Shepherd timer.
Date: Sun, 19 Jan 2025 23:04:00 +0100
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           |  39 +++++++++----
 gnu/services/backup.scm | 122 +++++++++++++++++++++++++++++++++-------
 2 files changed, 131 insertions(+), 30 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 0015d739bb6..33552065fe3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -111,7 +111,7 @@
 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@*
@@ -42709,19 +42709,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
 @end example
 
 @c %start of fragment
@@ -42752,6 +42749,23 @@ 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 be @file{/var/log/restic-backup/JOB-NAME.log}, where @code{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.
 
@@ -42764,9 +42778,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 <at> 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 555e9fc9590..3dda6ca370c 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 <goodoldpaul <at> autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul <at> autistici.org>
 ;;;
 ;;; 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 @@ (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,23 @@ (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 be @file{/var/log/restic-backup/JOB-NAME.log}, where @code{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 @@ (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 <at> 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 @@ (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 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 @@ (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 +316,5 @@ (define restic-backup-service-type
                                   jobs)))))
                 (default-value (restic-backup-configuration))
                 (description
-                 "This service configures @code{mcron} jobs for running backups
-with @code{restic}.")))
+                 "This service configures @code{Shepherd} timers for running backups
+with restic.")))

base-commit: 5e834c220e81fddb77a26e23cf0cd5055b866844
-- 
2.47.1





Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Fri, 24 Jan 2025 23:02:02 GMT) Full text and rfc822 format available.

Notification sent to Giacomo Leidi <goodoldpaul <at> autistici.org>:
bug acknowledged by developer. (Fri, 24 Jan 2025 23:02:02 GMT) Full text and rfc822 format available.

Message #24 received at 75045-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Giacomo Leidi <goodoldpaul <at> autistici.org>
Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>, 75045-done <at> debbugs.gnu.org
Subject: Re: [bug#75045] [PATCH v2] services: restic-backup: Implement as a
 Shepherd timer.
Date: Sat, 25 Jan 2025 00:01:45 +0100
[Message part 1 (text/plain, inline)]
Giacomo Leidi <goodoldpaul <at> autistici.org> skribis:

> 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

Applied with the cosmetic change below, thanks!

I just realized there’s no test for this service, so let’s everything
still works well.  Would be nice to add a test.

> +                            ;; 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))

Thanks for adding this comment.

Ludo’.

[Message part 2 (text/x-patch, inline)]
diff --git a/doc/guix.texi b/doc/guix.texi
index d18209f288..9a53bdcd37 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -42755,7 +42755,7 @@ Miscellaneous Services
 
 @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/JOB-NAME.log}, where @code{JOB-NAME} is the
+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)
diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm
index 3dda6ca370..6cada58b10 100644
--- a/gnu/services/backup.scm
+++ b/gnu/services/backup.scm
@@ -89,7 +89,7 @@ (define-configuration/no-serialization restic-backup-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/JOB-NAME.log}, where @code{JOB-NAME} is the
+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)
@@ -316,5 +316,5 @@ (define restic-backup-service-type
                                   jobs)))))
                 (default-value (restic-backup-configuration))
                 (description
-                 "This service configures @code{Shepherd} timers for running backups
+                 "This service configures Shepherd timers for running backups
 with restic.")))

Information forwarded to guix-patches <at> gnu.org:
bug#75045; Package guix-patches. (Fri, 24 Jan 2025 23:05:02 GMT) Full text and rfc822 format available.

Message #27 received at 75045 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Giacomo Leidi <goodoldpaul <at> autistici.org>
Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>, 75045 <at> debbugs.gnu.org
Subject: Re: [bug#75045] [PATCH v2] services: restic-backup: Implement as a
 Shepherd timer.
Date: Sat, 25 Jan 2025 00:04:03 +0100
[Message part 1 (text/plain, inline)]
And also:

[Message part 2 (text/x-patch, inline)]
-(define-maybe string)
-(define-maybe number)
+(define-maybe/no-serialization string)
+(define-maybe/no-serialization number)
[Message part 3 (text/plain, inline)]
… to silence a warning about ‘serialize-string’ etc. being unbound.

bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sat, 22 Feb 2025 12:24:27 GMT) Full text and rfc822 format available.

This bug report was last modified 19 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.