summaryrefslogtreecommitdiff
path: root/gnu/services
diff options
context:
space:
mode:
authorGiacomo Leidi <goodoldpaul@autistici.org>2025-05-17 17:09:54 +0200
committerLudovic Courtès <ludo@gnu.org>2025-05-18 22:55:53 +0200
commit1220d1a84eaaa8a60a457fa378cd986ec2269c08 (patch)
tree83fd23a403c792646b7afc0a2a9f380cd1b986c5 /gnu/services
parent86022e994e5fcb3918f2d3d2f6f89b24c5562910 (diff)
home: Add home-restic-backup service.
* gnu/services/backup.scm: Drop mcron obsolete export. (restic-backup-job-program): Generalize to restic-program. (lower-restic-backup-job): New procedure implementing a standard way to lower restic-backup-job records into lists. (restic-program): Implement general way to run restic commands, for example to initialize repositories. (restic-backup-configuration): Reimplement with (guix records). (restic-backup-job-{logfile,command,requirement,modules}): Add new procedures and add support for Guix Home environments. (restic-backup-job->shepherd-service): Add support for Guix Home environments. (restic-backup-service-activation): Drop procedure as now the Shepherd takes care of creating timers log file directories. (restic-backup-service-type): Drop profile and activation services extensions. * gnu/home/services/backup.scm: New file. * gnu/local.mk: Add this. * doc/guix.texi: Document this. Change-Id: Ied1c0a5756b715fba176a0e42ea154246089e6be Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Diffstat (limited to 'gnu/services')
-rw-r--r--gnu/services/backup.scm187
1 files changed, 125 insertions, 62 deletions
diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm
index 6e066bd3d66..8fdf9ce902f 100644
--- a/gnu/services/backup.scm
+++ b/gnu/services/backup.scm
@@ -28,6 +28,7 @@
28 #:prefix license:) 28 #:prefix license:)
29 #:use-module (guix modules) 29 #:use-module (guix modules)
30 #:use-module (guix packages) 30 #:use-module (guix packages)
31 #:use-module (guix records)
31 #:use-module (srfi srfi-1) 32 #:use-module (srfi srfi-1)
32 #:export (restic-backup-job 33 #:export (restic-backup-job
33 restic-backup-job? 34 restic-backup-job?
@@ -47,16 +48,21 @@
47 restic-backup-job-verbose? 48 restic-backup-job-verbose?
48 restic-backup-job-extra-flags 49 restic-backup-job-extra-flags
49 50
51 lower-restic-backup-job
52
50 restic-backup-configuration 53 restic-backup-configuration
51 restic-backup-configuration? 54 restic-backup-configuration?
52 restic-backup-configuration-fields
53 restic-backup-configuration-jobs 55 restic-backup-configuration-jobs
54 56
55 restic-backup-job-program 57 restic-backup-job-program
56 restic-backup-job->mcron-job 58 restic-backup-job->shepherd-service
57 restic-guix 59 restic-guix
58 restic-guix-wrapper-package 60 restic-guix-wrapper-package
59 restic-backup-service-profile 61 restic-backup-service-profile
62 restic-program
63 restic-job-log-file
64 restic-backup-job-command
65 restic-backup-job-modules
60 restic-backup-service-type)) 66 restic-backup-service-type))
61 67
62(define (gexp-or-string? value) 68(define (gexp-or-string? value)
@@ -75,6 +81,8 @@
75 81
76(define-maybe/no-serialization string) 82(define-maybe/no-serialization string)
77(define-maybe/no-serialization number) 83(define-maybe/no-serialization number)
84(define-maybe/no-serialization symbol)
85(define-maybe/no-serialization list-of-symbols)
78 86
79(define-configuration/no-serialization restic-backup-job 87(define-configuration/no-serialization restic-backup-job
80 (restic 88 (restic
@@ -90,7 +98,8 @@
90 (maybe-string) 98 (maybe-string)
91 "The file system path to the log file for this job. By default the file will 99 "The file system path to the log file for this job. By default the file will
92have be @file{/var/log/restic-backup/@var{job-name}.log}, where @var{job-name} is the 100have be @file{/var/log/restic-backup/@var{job-name}.log}, where @var{job-name} is the
93name defined in the @code{name} field.") 101name defined in the @code{name} field. For Guix Home services it defaults to
102@file{$XDG_STATE_HOME/shepherd/restic-backup/@var{job-name}.log}.")
94 (max-duration 103 (max-duration
95 (maybe-number) 104 (maybe-number)
96 "The maximum duration in seconds that a job may last. Past 105 "The maximum duration in seconds that a job may last. Past
@@ -117,8 +126,10 @@ current job.")
117evaluate to @code{calendar-event} records or to strings. Strings must contain 126evaluate to @code{calendar-event} records or to strings. Strings must contain
118Vixie cron date lines.") 127Vixie cron date lines.")
119 (requirement 128 (requirement
120 (list-of-symbols '()) 129 (maybe-list-of-symbols)
121 "The list of Shepherd services that this backup job depends upon.") 130 "The list of Shepherd services that this backup job depends upon. When unset it
131defaults to @code{'()}, for Guix Home. Otherwise to
132@code{'(user-processes file-systems)}.")
122 (files 133 (files
123 (list-of-lowerables '()) 134 (list-of-lowerables '())
124 "The list of files or directories to be backed up. It must be a list of 135 "The list of files or directories to be backed up. It must be a list of
@@ -131,15 +142,20 @@ values that can be lowered to strings.")
131 "A list of values that are lowered to strings. These will be passed as 142 "A list of values that are lowered to strings. These will be passed as
132command-line arguments to the current job @command{restic backup} invocation.")) 143command-line arguments to the current job @command{restic backup} invocation."))
133 144
134(define list-of-restic-backup-jobs? 145;; (for-home (restic-backup-configuration ...)) is not able to replace for-home? with #t,
135 (list-of restic-backup-job?)) 146;; pk prints #f. Once for-home will be able to work with (gnu services configuration) the
147;; record can be migrated back to define-configuration.
148(define-record-type* <restic-backup-configuration>
149 restic-backup-configuration
150 make-restic-backup-configuration
151 restic-backup-configuration?
152 this-restic-backup-configuration
136 153
137(define-configuration/no-serialization restic-backup-configuration 154 (jobs restic-backup-configuration-jobs (default '())) ; list of restic-backup-job
138 (jobs 155 (home-service? restic-backup-configuration-home-service?
139 (list-of-restic-backup-jobs '()) 156 (default for-home?) (innate)))
140 "The list of backup jobs for the current system."))
141 157
142(define (restic-backup-job-program config) 158(define (lower-restic-backup-job config)
143 (let ((restic 159 (let ((restic
144 (file-append (restic-backup-job-restic config) "/bin/restic")) 160 (file-append (restic-backup-job-restic config) "/bin/restic"))
145 (repository 161 (repository
@@ -150,22 +166,42 @@ command-line arguments to the current job @command{restic backup} invocation."))
150 (restic-backup-job-files config)) 166 (restic-backup-job-files config))
151 (extra-flags 167 (extra-flags
152 (restic-backup-job-extra-flags config)) 168 (restic-backup-job-extra-flags config))
153 (verbose 169 (verbose?
154 (if (restic-backup-job-verbose? config) 170 (if (restic-backup-job-verbose? config)
155 '("--verbose") 171 '("--verbose")
156 '()))) 172 '())))
157 (program-file 173 #~(list (list #$@files) #$restic #$repository #$password-file
158 "restic-backup-job.scm" 174 (list #$@verbose?) (list #$@extra-flags))))
159 #~(begin 175
160 (use-modules (ice-9 popen) 176(define restic-program
161 (ice-9 rdelim)) 177 #~(lambda (action action-args job-restic repository password-file verbose? extra-flags)
162 (setenv "RESTIC_PASSWORD" 178 (use-modules (ice-9 format))
163 (with-input-from-file #$password-file read-line)) 179 ;; This can be extended later, i.e. to have a
164 180 ;; centrally defined restic package.
165 (execlp #$restic #$restic #$@verbose 181 ;; See https://issues.guix.gnu.org/71639
166 "-r" #$repository 182 (define restic job-restic)
167 #$@extra-flags 183
168 "backup" #$@files))))) 184 (define command
185 `(,restic ,@verbose?
186 "-r" ,repository
187 ,@extra-flags
188 ,action ,@action-args))
189
190 (setenv "RESTIC_PASSWORD_FILE" password-file)
191
192 (when (> (length verbose?) 0)
193 (format #t "Running~{ ~a~}~%" command))
194
195 (apply execlp `(,restic ,@command))))
196
197(define (restic-backup-job-program config)
198 (program-file
199 "restic-backup"
200 #~(let ((restic-exec
201 #$restic-program)
202 (job #$(lower-restic-backup-job config)))
203
204 (apply restic-exec `("backup" ,@job)))))
169 205
170(define (restic-guix jobs) 206(define (restic-guix jobs)
171 (program-file 207 (program-file
@@ -207,55 +243,89 @@ command-line arguments to the current job @command{restic backup} invocation."))
207 243
208 (main (command-line))))) 244 (main (command-line)))))
209 245
210(define (restic-job-log-file job) 246(define* (restic-job-log-file job #:key (home-service? #f))
211 (let ((name (restic-backup-job-name job)) 247 (let ((name (restic-backup-job-name job))
212 (log-file (restic-backup-job-log-file job))) 248 (log-file (restic-backup-job-log-file job)))
213 (if (maybe-value-set? log-file) 249 (if (maybe-value-set? log-file)
214 log-file 250 log-file
215 (string-append "/var/log/restic-backup/" name ".log")))) 251 (if home-service?
252 #~(begin
253 (use-modules (shepherd support))
254 (string-append %user-log-dir "/restic-backup/" #$name ".log"))
255 (string-append "/var/log/restic-backup/" name ".log")))))
256
257(define* (restic-backup-job-command name files #:key (home-service? #f))
258 (if home-service?
259 #~(list
260 "restic-guix" "backup" #$name)
261 ;; We go through bash, instead of executing
262 ;; restic-guix directly, because the login shell
263 ;; gives us the correct user environment that some
264 ;; backends require, such as rclone.
265 #~(list
266 (string-append #$bash-minimal "/bin/bash")
267 "-l" "-c"
268 (string-append "restic-guix backup " #$name))))
216 269
217(define (restic-backup-job->shepherd-service config) 270(define* (restic-job-requirement config #:key (home-service? #f))
271 (define maybe-requirement (restic-backup-job-requirement config))
272 (if (maybe-value-set? maybe-requirement)
273 maybe-requirement
274 (if home-service?
275 '()
276 '(user-processes file-systems))))
277
278(define* (restic-backup-job-modules #:key (home-service? #f))
279 `((shepherd service timer)
280 ,@(if home-service?
281 ;;for %user-log-dir
282 '((shepherd support))
283 '())))
284
285(define* (restic-backup-job->shepherd-service config #:key (home-service? #f))
218 (let ((schedule (restic-backup-job-schedule config)) 286 (let ((schedule (restic-backup-job-schedule config))
219 (name (restic-backup-job-name config)) 287 (name (restic-backup-job-name config))
288 (files (restic-backup-job-files config))
220 (user (restic-backup-job-user config)) 289 (user (restic-backup-job-user config))
221 (group (restic-backup-job-group config)) 290 (group (restic-backup-job-group config))
222 (max-duration (restic-backup-job-max-duration config)) 291 (max-duration (restic-backup-job-max-duration config))
223 (wait-for-termination? (restic-backup-job-wait-for-termination? config)) 292 (wait-for-termination? (restic-backup-job-wait-for-termination? config))
224 (log-file (restic-job-log-file config)) 293 (log-file (restic-job-log-file
225 (requirement (restic-backup-job-requirement config))) 294 config #:home-service? home-service?))
295 (requirement
296 (restic-job-requirement config #:home-service? home-service?)))
226 (shepherd-service (provision `(,(string->symbol name))) 297 (shepherd-service (provision `(,(string->symbol name)))
227 (requirement 298 (requirement requirement)
228 `(user-processes file-systems ,@requirement))
229 (documentation 299 (documentation
230 "Run @code{restic} backed backups on a regular basis.") 300 "Run restic backed backups on a regular basis.")
231 (modules '((shepherd service timer))) 301 (modules (restic-backup-job-modules
302 #:home-service? home-service?))
232 (start 303 (start
233 #~(make-timer-constructor 304 #~(make-timer-constructor
234 (if (string? #$schedule) 305 (if (string? #$schedule)
235 (cron-string->calendar-event #$schedule) 306 (cron-string->calendar-event #$schedule)
236 #$schedule) 307 #$schedule)
237 (command 308 (command
238 (list 309 #$(restic-backup-job-command
239 ;; We go through bash, instead of executing 310 name files #:home-service? home-service?)
240 ;; restic-guix directly, because the login shell 311 #$@(if home-service? '() (list #:user user))
241 ;; gives us the correct user environment that some 312 #$@(if home-service? '() (list #:group group))
242 ;; backends require, such as rclone. 313 #$@(if home-service? '()
243 (string-append #+bash-minimal "/bin/bash") 314 (list
244 "-l" "-c" 315 #:environment-variables
245 (string-append "restic-guix backup " #$name)) 316 #~(list
246 #:user #$user 317 (string-append
247 #:group #$group 318 "HOME=" (passwd:dir (getpwnam #$user)))))))
248 #:environment-variables
249 (list
250 (string-append
251 "HOME=" (passwd:dir (getpwnam #$user)))))
252 #:log-file #$log-file 319 #:log-file #$log-file
253 #:wait-for-termination? #$wait-for-termination? 320 #:wait-for-termination? #$wait-for-termination?
254 #:max-duration #$(and (maybe-value-set? max-duration) 321 #:max-duration #$(and (maybe-value-set? max-duration)
255 max-duration))) 322 max-duration)))
256 (stop 323 (stop
257 #~(make-timer-destructor)) 324 #~(make-timer-destructor))
258 (actions (list shepherd-trigger-action))))) 325 (actions (list (shepherd-action
326 (inherit shepherd-trigger-action)
327 (documentation "Manually trigger a backup,
328without waiting for the scheduled time.")))))))
259 329
260(define (restic-guix-wrapper-package jobs) 330(define (restic-guix-wrapper-package jobs)
261 (package 331 (package
@@ -283,26 +353,19 @@ without waiting for the scheduled job to run.")
283 (restic-guix-wrapper-package jobs)) 353 (restic-guix-wrapper-package jobs))
284 '()))) 354 '())))
285 355
286(define (restic-backup-activation config)
287 #~(for-each
288 (lambda (log-file)
289 (mkdir-p (dirname log-file)))
290 (list #$@(map restic-job-log-file
291 (restic-backup-configuration-jobs config)))))
292
293(define restic-backup-service-type 356(define restic-backup-service-type
294 (service-type (name 'restic-backup) 357 (service-type (name 'restic-backup)
295 (extensions 358 (extensions
296 (list 359 (list
297 (service-extension activation-service-type
298 restic-backup-activation)
299 (service-extension profile-service-type 360 (service-extension profile-service-type
300 restic-backup-service-profile) 361 restic-backup-service-profile)
301 (service-extension shepherd-root-service-type 362 (service-extension shepherd-root-service-type
302 (lambda (config) 363 (match-record-lambda <restic-backup-configuration>
303 (map restic-backup-job->shepherd-service 364 (jobs home-service?)
304 (restic-backup-configuration-jobs 365 (map (lambda (job)
305 config)))))) 366 (restic-backup-job->shepherd-service
367 job #:home-service? home-service?))
368 jobs)))))
306 (compose concatenate) 369 (compose concatenate)
307 (extend 370 (extend
308 (lambda (config jobs) 371 (lambda (config jobs)