mirror of
https://codeberg.org/guix/guix.git
synced 2025-10-02 02:15:12 +00:00
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 <ludo@gnu.org>
This commit is contained in:
parent
44d12f9663
commit
35c6ae6e58
2 changed files with 131 additions and 30 deletions
|
@ -111,7 +111,7 @@ Copyright @copyright{} 2022 (@*
|
||||||
Copyright @copyright{} 2022 John Kehayias@*
|
Copyright @copyright{} 2022 John Kehayias@*
|
||||||
Copyright @copyright{} 2022–2023 Bruno Victal@*
|
Copyright @copyright{} 2022–2023 Bruno Victal@*
|
||||||
Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
|
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{} 2022 Antero Mejr@*
|
||||||
Copyright @copyright{} 2023 Karl Hallsby@*
|
Copyright @copyright{} 2023 Karl Hallsby@*
|
||||||
Copyright @copyright{} 2023 Nathaniel Nicandro@*
|
Copyright @copyright{} 2023 Nathaniel Nicandro@*
|
||||||
|
@ -42710,19 +42710,16 @@ following configuration:
|
||||||
"/etc/guix/signing-key.sec"))))))))))
|
"/etc/guix/signing-key.sec"))))))))))
|
||||||
@end lisp
|
@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
|
@env{RESTIC_PASSWORD} environment variable by reading the first line of
|
||||||
@code{password-file} and runs @command{restic backup}, creating backups
|
@code{password-file} and runs @command{restic backup}, creating backups
|
||||||
using rclone of all the files listed in the @code{files} field.
|
using rclone of all the files listed in the @code{files} field.
|
||||||
|
|
||||||
The @code{restic-backup-service-type} installs as well @code{restic-guix}
|
The @code{restic-backup-service-type} provides the ability to instantaneously
|
||||||
to the system profile, a @code{restic} utility wrapper that allows for easier
|
trigger a backup with the @code{trigger} Shepherd action:
|
||||||
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:
|
|
||||||
|
|
||||||
@example
|
@example
|
||||||
restic-guix backup remote-ftp
|
sudo herd trigger remote-ftp
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@c %start of fragment
|
@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)
|
@item @code{user} (default: @code{"root"}) (type: string)
|
||||||
The user used for running the current job.
|
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)
|
@item @code{repository} (type: string)
|
||||||
The restic repository target of this job.
|
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.
|
for the current job.
|
||||||
|
|
||||||
@item @code{schedule} (type: gexp-or-string)
|
@item @code{schedule} (type: gexp-or-string)
|
||||||
A string or a gexp that will be passed as time specification in the
|
A string or a gexp representing the frequency of the backup. Gexp must
|
||||||
mcron job specification (@pxref{Syntax, mcron job specifications,,
|
evaluate to @code{calendar-event} records or to strings. Strings must contain
|
||||||
mcron,GNU@tie{}mcron}).
|
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)
|
@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
|
The list of files or directories to be backed up. It must be a list of
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;; GNU Guix --- Functional package management for GNU
|
;;; GNU Guix --- Functional package management for GNU
|
||||||
;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
|
;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
|
@ -18,9 +18,10 @@
|
||||||
|
|
||||||
(define-module (gnu services backup)
|
(define-module (gnu services backup)
|
||||||
#:use-module (gnu packages backup)
|
#:use-module (gnu packages backup)
|
||||||
|
#:use-module (gnu packages bash)
|
||||||
#:use-module (gnu services)
|
#:use-module (gnu services)
|
||||||
#:use-module (gnu services configuration)
|
#:use-module (gnu services configuration)
|
||||||
#:use-module (gnu services mcron)
|
#:use-module (gnu services shepherd)
|
||||||
#:use-module (guix build-system copy)
|
#:use-module (guix build-system copy)
|
||||||
#:use-module (guix gexp)
|
#:use-module (guix gexp)
|
||||||
#:use-module ((guix licenses)
|
#:use-module ((guix licenses)
|
||||||
|
@ -33,11 +34,16 @@
|
||||||
restic-backup-job-fields
|
restic-backup-job-fields
|
||||||
restic-backup-job-restic
|
restic-backup-job-restic
|
||||||
restic-backup-job-user
|
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-name
|
||||||
restic-backup-job-repository
|
restic-backup-job-repository
|
||||||
restic-backup-job-password-file
|
restic-backup-job-password-file
|
||||||
restic-backup-job-schedule
|
restic-backup-job-schedule
|
||||||
restic-backup-job-files
|
restic-backup-job-files
|
||||||
|
restic-backup-job-requirement
|
||||||
restic-backup-job-verbose?
|
restic-backup-job-verbose?
|
||||||
restic-backup-job-extra-flags
|
restic-backup-job-extra-flags
|
||||||
|
|
||||||
|
@ -64,6 +70,12 @@
|
||||||
(define list-of-lowerables?
|
(define list-of-lowerables?
|
||||||
(list-of lowerable?))
|
(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
|
(define-configuration/no-serialization restic-backup-job
|
||||||
(restic
|
(restic
|
||||||
(package restic)
|
(package restic)
|
||||||
|
@ -71,6 +83,23 @@
|
||||||
(user
|
(user
|
||||||
(string "root")
|
(string "root")
|
||||||
"The user used for running the current job.")
|
"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
|
(name
|
||||||
(string)
|
(string)
|
||||||
"A string denoting a name for this job.")
|
"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.")
|
current job.")
|
||||||
(schedule
|
(schedule
|
||||||
(gexp-or-string)
|
(gexp-or-string)
|
||||||
"A string or a gexp that will be passed as time specification in the mcron
|
"A string or a gexp representing the frequency of the backup. Gexp must
|
||||||
job specification (@pxref{Syntax, mcron job specifications,, mcron,
|
evaluate to @code{calendar-event} records or to strings. Strings must contain
|
||||||
GNU@tie{}mcron}).")
|
Vixie cron date lines.")
|
||||||
|
(requirement
|
||||||
|
(list-of-symbols '())
|
||||||
|
"The list of Shepherd services that this backup job depends upon.")
|
||||||
(files
|
(files
|
||||||
(list-of-lowerables '())
|
(list-of-lowerables '())
|
||||||
"The list of files or directories to be backed up. It must be a list of
|
"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)))))
|
(main (command-line)))))
|
||||||
|
|
||||||
(define (restic-backup-job->mcron-job config)
|
(define (restic-job-log-file job)
|
||||||
(let ((user
|
(let ((name (restic-backup-job-name job))
|
||||||
(restic-backup-job-user config))
|
(log-file (restic-backup-job-log-file job)))
|
||||||
(schedule
|
(if (maybe-value-set? log-file)
|
||||||
(restic-backup-job-schedule config))
|
log-file
|
||||||
(name
|
(string-append "/var/log/restic-backup/" name ".log"))))
|
||||||
(restic-backup-job-name config)))
|
|
||||||
#~(job #$schedule
|
(define (restic-backup-job->shepherd-service config)
|
||||||
#$(string-append "restic-guix backup " name)
|
(let ((schedule (restic-backup-job-schedule config))
|
||||||
#:user #$user)))
|
(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)
|
(define (restic-guix-wrapper-package jobs)
|
||||||
(package
|
(package
|
||||||
|
@ -212,15 +287,24 @@ without waiting for the scheduled job to run.")
|
||||||
(restic-guix-wrapper-package jobs))
|
(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
|
(define restic-backup-service-type
|
||||||
(service-type (name 'restic-backup)
|
(service-type (name 'restic-backup)
|
||||||
(extensions
|
(extensions
|
||||||
(list
|
(list
|
||||||
|
(service-extension activation-service-type
|
||||||
|
restic-backup-activation)
|
||||||
(service-extension profile-service-type
|
(service-extension profile-service-type
|
||||||
restic-backup-service-profile)
|
restic-backup-service-profile)
|
||||||
(service-extension mcron-service-type
|
(service-extension shepherd-root-service-type
|
||||||
(lambda (config)
|
(lambda (config)
|
||||||
(map restic-backup-job->mcron-job
|
(map restic-backup-job->shepherd-service
|
||||||
(restic-backup-configuration-jobs
|
(restic-backup-configuration-jobs
|
||||||
config))))))
|
config))))))
|
||||||
(compose concatenate)
|
(compose concatenate)
|
||||||
|
@ -232,5 +316,5 @@ without waiting for the scheduled job to run.")
|
||||||
jobs)))))
|
jobs)))))
|
||||||
(default-value (restic-backup-configuration))
|
(default-value (restic-backup-configuration))
|
||||||
(description
|
(description
|
||||||
"This service configures @code{mcron} jobs for running backups
|
"This service configures Shepherd timers for running backups
|
||||||
with @code{restic}.")))
|
with restic.")))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue