From patchwork Sat Mar 8 17:40:23 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: paul X-Patchwork-Id: 39944 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id 2D66727BBEC; Sat, 8 Mar 2025 17:42:28 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_CERTIFIED,RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE, SPF_HELO_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id 58A7A27BBE2 for ; Sat, 8 Mar 2025 17:42:27 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tqyBb-0002y2-Kc; Sat, 08 Mar 2025 12:42:07 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tqyBX-0002x7-Fd for guix-patches@gnu.org; Sat, 08 Mar 2025 12:42:03 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tqyBW-0001zk-PQ; Sat, 08 Mar 2025 12:42:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:In-Reply-To:References:Subject; bh=ltLfXlcnMdEJ+3XpsJANQe9oZu7kEJ/0yjn/ESxMOxU=; b=INfFilCoJTLWtmQQEb9PT6TdoBPIv35uDv8Rf/yz4htCSbqoYBHZJH5wMQHJRfkdlqFFbxuhaOfr48Hh5rXiVkHvlGRYgDeCSZwQWn1zqesXS/HvviOVV/ro0yTvEqwgAv2AktMSGv22hERRbRZrGHsHSHlZGZcAMMGcrx6V+yFNYwADQxn1yAGZ1INXZIJ3+7GpHcEQiiffQNICCXmQX+FpOYD8FfmvHBeZ5PP2q/JMid8oy1QQ3Bd7wWCv+tGpIpWkRR1lhoeF1gsuPTpa/B/svbabogvuJz+fPRNmP8/gMEJkNM7frnRUpxdF973pshcLDoZP1DaJUn/OYfdN8g==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tqyBW-0003Ho-Ie; Sat, 08 Mar 2025 12:42:02 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#76169] [PATCH v6 1/2] services: restic-backup: Add more restic commands to the restic-guix package. References: <76d82b80-722b-46de-8791-d6a19def8c85@autistici.org> In-Reply-To: <76d82b80-722b-46de-8791-d6a19def8c85@autistici.org> Resent-From: Giacomo Leidi Original-Sender: "Debbugs-submit" Resent-CC: ludo@gnu.org, maxim.cournoyer@gmail.com, guix-patches@gnu.org Resent-Date: Sat, 08 Mar 2025 17:42:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 76169 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 76169@debbugs.gnu.org Cc: Giacomo Leidi , Ludovic =?utf-8?q?Court=C3=A8?= =?utf-8?q?s?= , Maxim Cournoyer X-Debbugs-Original-Xcc: Ludovic =?utf-8?q?Court=C3=A8s?= , Maxim Cournoyer Received: via spool by 76169-submit@debbugs.gnu.org id=B76169.174145566712528 (code B ref 76169); Sat, 08 Mar 2025 17:42:02 +0000 Received: (at 76169) by debbugs.gnu.org; 8 Mar 2025 17:41:07 +0000 Received: from localhost ([127.0.0.1]:56683 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tqyAc-0003G0-BH for submit@debbugs.gnu.org; Sat, 08 Mar 2025 12:41:07 -0500 Received: from confino.investici.org ([93.190.126.19]:42163) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1tqyAZ-0003FO-T5 for 76169@debbugs.gnu.org; Sat, 08 Mar 2025 12:41:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=autistici.org; s=stigmate; t=1741455663; bh=ltLfXlcnMdEJ+3XpsJANQe9oZu7kEJ/0yjn/ESxMOxU=; h=From:To:Cc:Subject:Date:From; b=pgPDyJQehl5F+xFh982ItyRpcwflR8OTCf61jg7ihUpkv7ma03FiHpmveZY1Lmcvz d4azeJa60WoNL+XSccVo4GaCDQ+9NK4olHHEduHvqldBttWq2e4QeRcqqxQ1M5xjtS em/BoK1xFgy8JZs0+ynVMvhXIiZzB/5ZMhN/ZAJQ= Received: from mx1.investici.org (unknown [127.0.0.1]) by confino.investici.org (Postfix) with ESMTP id 4Z99Rz00rZz11Pv; Sat, 8 Mar 2025 17:41:02 +0000 (UTC) Received: from [93.190.126.19] (mx1.investici.org [93.190.126.19]) (Authenticated sender: goodoldpaul@autistici.org) by localhost (Postfix) with ESMTPSA id 4Z99Ry5xrnz10xY; Sat, 8 Mar 2025 17:41:02 +0000 (UTC) Date: Sat, 8 Mar 2025 18:40:23 +0100 Message-ID: <3dbfa227b49e6f0cd7634d5d42fac38a549c0062.1741455624.git.goodoldpaul@autistici.org> X-Mailer: git-send-email 2.48.1 MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Giacomo Leidi X-ACL-Warn: , Giacomo Leidi via Guix-patches X-Patchwork-Original-From: Giacomo Leidi via Guix-patches via From: paul Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches This patch refactors the way restic commands can be added to the restic-guix package with a more general approach. This way new subcommands for restic-guix can be added more easily. * gnu/services/backup.scm (restic-backup-job-program): Generalize to restic-program; (restic-guix): allow for multiple actions. * doc/guix.texi: Document it. Change-Id: Ib2b5d74bebc51e35f1ae6e1aa32cedee0da59697 --- doc/guix.texi | 20 +++++- gnu/services/backup.scm | 138 ++++++++++++++++++++++++++-------------- 2 files changed, 108 insertions(+), 50 deletions(-) base-commit: be49b0f4435cf1d9275cbbc9cac3a84fa4478ff9 diff --git a/doc/guix.texi b/doc/guix.texi index 6529865c090..321c312fa83 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -43798,6 +43798,23 @@ Miscellaneous Services sudo herd trigger remote-ftp @end example +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 list all the shapshots available on a given job's repository: + +@example +restic-guix snapshots remote-ftp +@end example + +All arguments passed after the job name will be passed to the underlying +@code{restic} command, together with the @code{extra-flags} field from the +@code{restic-backup-job} record: + +@example +restic-guix restore remote-ftp -t `pwd`/restored -i .config/guix/channels.scm latest +@end example + @c %start of fragment @deftp {Data Type} restic-backup-configuration @@ -43871,8 +43888,7 @@ Miscellaneous Services @item @code{extra-flags} (default: @code{'()}) (type: list-of-lowerables) A list of values that are lowered to strings. These will be passed as -command-line arguments to the current job @command{restic backup} -invocation. +command-line arguments to the current @command{restic} invocation. @end table diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm index 4d8cf167f04..dc095593432 100644 --- a/gnu/services/backup.scm +++ b/gnu/services/backup.scm @@ -52,11 +52,12 @@ (define-module (gnu services backup) restic-backup-configuration-fields restic-backup-configuration-jobs - restic-backup-job-program - restic-backup-job->mcron-job + restic-program + restic-backup-job->shepherd-service restic-guix restic-guix-wrapper-package restic-backup-service-profile + restic-backup-service-activation restic-backup-service-type)) (define (gexp-or-string? value) @@ -129,7 +130,7 @@ (define-configuration/no-serialization restic-backup-job (extra-flags (list-of-lowerables '()) "A list of values that are lowered to strings. These will be passed as -command-line arguments to the current job @command{restic backup} invocation.")) +command-line arguments to the current @command{restic} invocation.")) (define list-of-restic-backup-jobs? (list-of restic-backup-job?)) @@ -139,71 +140,107 @@ (define-configuration/no-serialization restic-backup-configuration (list-of-restic-backup-jobs '()) "The list of backup jobs for the current system.")) -(define (restic-backup-job-program config) +(define %restic-guix-supported-actions + '("backup" "list" "ls" "mount" "prune" "restore" "snapshots" "unlock")) + +(define (restic-backup-job->kv config) (let ((restic (file-append (restic-backup-job-restic config) "/bin/restic")) + (name + (restic-backup-job-name config)) (repository (restic-backup-job-repository config)) (password-file (restic-backup-job-password-file config)) - (files - (restic-backup-job-files config)) (extra-flags (restic-backup-job-extra-flags config)) - (verbose + (verbose? (if (restic-backup-job-verbose? config) '("--verbose") '()))) - (program-file - "restic-backup-job.scm" - #~(begin - (use-modules (ice-9 popen) - (ice-9 rdelim)) - (setenv "RESTIC_PASSWORD" - (with-input-from-file #$password-file read-line)) + #~(list #$name (list #$restic #$repository #$password-file + (list #$@verbose?) (list #$@extra-flags))))) + +(define (restic-program config) + #~(lambda* (action action-args job-restic repository password-file verbose? extra-flags) + (use-modules (ice-9 format) + (ice-9 popen) + (ice-9 rdelim)) + ;; This can be extended later, i.e. to have a + ;; centrally defined restic package. + ;; See https://issues.guix.gnu.org/71639 + (define restic job-restic) + + (define command + `(,restic ,@verbose? + "-r" ,repository + ,@extra-flags + ,action ,@action-args)) + + (setenv "RESTIC_PASSWORD" + (with-input-from-file password-file read-line)) - (execlp #$restic #$restic #$@verbose - "-r" #$repository - #$@extra-flags - "backup" #$@files))))) + (when (> (length verbose?) 0) + (format #t "Running~{ ~a~}~%" command)) -(define (restic-guix jobs) + (apply execlp `(,restic ,@command)))) + +(define* (restic-guix config #:key (supported-actions + %restic-guix-supported-actions)) (program-file "restic-guix" #~(begin (use-modules (ice-9 match) (srfi srfi-1)) - (define names '#$(map restic-backup-job-name jobs)) - (define programs '#$(map restic-backup-job-program jobs)) - - (define (get-program name) - (define idx - (list-index (lambda (n) (string=? n name)) names)) - (unless idx - (error (string-append "Unknown job name " name "\n\n" - "Possible job names are: " - (string-join names " ")))) - (list-ref programs idx)) + (define jobs + (list + #$@(map restic-backup-job->kv + (restic-backup-configuration-jobs config)))) + (define names (map first jobs)) + (define (get-job key) + (first + (filter-map + (match-lambda + ((k v) + (and (string=? key k) v))) + jobs))) - (define (backup args) - (define name (third args)) - (define program (get-program name)) - (execlp program program)) + (define restic-exec + #$(restic-program config)) (define (validate-args args) - (when (not (>= (length args) 3)) - (error (string-append "Usage: " (basename (car args)) - " backup NAME")))) + (unless (>= (length args) 2) + (error (string-append "Usage: " (basename (first args)) + " ACTION [ARGS]\n\nSupported actions are: " + #$(string-join supported-actions ", ") "."))) + (unless (member (second args) '#$supported-actions) + (error (string-append "Unknown action: " (second args) ". Supported" + "actions are: " + #$(string-join supported-actions ", ") ".")))) + + (define (validate-action-args action args) + (define argc (length args)) + (when (not (>= argc 3)) + (error (string-append "Usage: " (basename (first args)) + " " action " JOB_NAME [ARGS]\n\nPossible job " + "names are: " (string-join names ", ") "."))) + (define job-name (third args)) + (unless (member job-name names) + (error (string-append "Unknown job name: " job-name ". Possible job " + "names are: " (string-join names ", ") "."))) + (let ((job (get-job job-name)) + (action-args + (if (> argc 3) + (take-right args (- argc 3)) + '()))) + (values job action-args))) (define (main args) (validate-args args) (define action (second args)) - (match action - ("backup" - (backup args)) - (_ - (error (string-append "Unknown action: " action))))) + (define-values (job action-args) (validate-action-args action args)) + (apply restic-exec `(,action ,action-args ,@job))) (main (command-line))))) @@ -217,6 +254,10 @@ (define (restic-job-log-file job) (define (restic-backup-job->shepherd-service config) (let ((schedule (restic-backup-job-schedule config)) (name (restic-backup-job-name config)) + (files (string-join + (map (lambda (f) (string-append "'" f "'")) + (restic-backup-job-files config)) + " ")) (user (restic-backup-job-user config)) (group (restic-backup-job-group config)) (max-duration (restic-backup-job-max-duration config)) @@ -242,7 +283,8 @@ (define (restic-backup-job->shepherd-service config) ;; backends require, such as rclone. (string-append #+bash-minimal "/bin/bash") "-l" "-c" - (string-append "restic-guix backup " #$name)) + (string-append + "restic-guix backup " #$name " " #$files)) #:user #$user #:group #$group #:environment-variables @@ -261,11 +303,11 @@ (define (restic-backup-job->shepherd-service config) without waiting for the scheduled time.") (procedure #~trigger-timer))))))) -(define (restic-guix-wrapper-package jobs) +(define (restic-guix-wrapper-package config) (package (name "restic-backup-service-wrapper") (version "0.0.0") - (source (restic-guix jobs)) + (source (restic-guix config)) (build-system copy-build-system) (arguments (list #:install-plan #~'(("./" "/bin")))) @@ -284,10 +326,10 @@ (define restic-backup-service-profile (define jobs (restic-backup-configuration-jobs config)) (if (> (length jobs) 0) (list - (restic-guix-wrapper-package jobs)) + (restic-guix-wrapper-package config)) '()))) -(define (restic-backup-activation config) +(define (restic-backup-service-activation config) #~(for-each (lambda (log-file) (mkdir-p (dirname log-file))) @@ -299,7 +341,7 @@ (define restic-backup-service-type (extensions (list (service-extension activation-service-type - restic-backup-activation) + restic-backup-service-activation) (service-extension profile-service-type restic-backup-service-profile) (service-extension shepherd-root-service-type From patchwork Sat Mar 8 17:40:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: paul X-Patchwork-Id: 39945 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id B552E27BBE2; Sat, 8 Mar 2025 17:42:29 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_CERTIFIED,RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE, SPF_HELO_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id B461127BBEA for ; Sat, 8 Mar 2025 17:42:27 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tqyBf-0002yf-3P; Sat, 08 Mar 2025 12:42:11 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tqyBd-0002yQ-DM for guix-patches@gnu.org; Sat, 08 Mar 2025 12:42:09 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tqyBd-00020b-3C; Sat, 08 Mar 2025 12:42:09 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:References:In-Reply-To:Date:From:To:Subject; bh=uPUnrPFd/Tp9XiztQgzL10nWnkmnD9st9ljHgqpV/20=; b=CUGbB+yk6NigAV7Q13m7R/9flfnM5q8q+yfZ1smYsxQXUwv6I2hZM0xt5NaeEvZVHJKR618cjq+RcSAoC7nrLscWWM8V6C8P5PNJ/TFkqyi6ECP7PhXoIUnMENUkYj+AYfR/o74SamIXnO1J3l46XyIcnjpVDx7g9i4wKdFUQVWHKmFdSzBMld1+TwYt7dzEOs/suA4WpNCu8LSzKcKqlmJXgrSwx09NGrCehm8HRBjCvWvhuMWLW8vl6HTIyxF8rFrjTfz+0F+hZydZ2cbcBRxGJaqADnR+rDAZh6U31UcIlyOQIwO+Debrf9iXEYjP4MQCZtRgkRjx0+eAPx/89w==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tqyBX-0003Hw-4j; Sat, 08 Mar 2025 12:42:03 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#76169] [PATCH v6 2/2] home: Add home-restic-backup service. Resent-From: Giacomo Leidi Original-Sender: "Debbugs-submit" Resent-CC: andrew@trop.in, janneke@gnu.org, ludo@gnu.org, maxim.cournoyer@gmail.com, tanguy@bioneland.org, guix-patches@gnu.org Resent-Date: Sat, 08 Mar 2025 17:42:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 76169 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 76169@debbugs.gnu.org Cc: Giacomo Leidi , Andrew Tropin , Janneke Nieuwenhuizen , Ludovic =?utf-8?q?Court=C3=A8s?= , Maxim Cournoyer , Tanguy Le Carrour X-Debbugs-Original-Xcc: Andrew Tropin , Janneke Nieuwenhuizen , Ludovic =?utf-8?q?Court=C3=A8s?= , Maxim Cournoyer , Tanguy Le Carrour Received: via spool by 76169-submit@debbugs.gnu.org id=B76169.174145567112551 (code B ref 76169); Sat, 08 Mar 2025 17:42:03 +0000 Received: (at 76169) by debbugs.gnu.org; 8 Mar 2025 17:41:11 +0000 Received: from localhost ([127.0.0.1]:56686 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tqyAg-0003GL-9U for submit@debbugs.gnu.org; Sat, 08 Mar 2025 12:41:11 -0500 Received: from confino.investici.org ([2a11:7980:1::2:0]:41747) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1tqyAc-0003FV-6W for 76169@debbugs.gnu.org; Sat, 08 Mar 2025 12:41:07 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=autistici.org; s=stigmate; t=1741455663; bh=uPUnrPFd/Tp9XiztQgzL10nWnkmnD9st9ljHgqpV/20=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SwfU7hV9lqFkmZlwduA/ILd/+rTw5dHHx5uDO6ok0Pauig/rvbvakUyv0O9/5aIma dNAJLVZCI4kpG4xTVPSajJYvWviTZps72ypGPCrwTa+G7tM/AmxgmV1JgBnnCi6pfD 3rKFcyQ5GHkKgH7pVGi7rfdE5XlhmuFFsS/QPFrc= Received: from mx1.investici.org (unknown [127.0.0.1]) by confino.investici.org (Postfix) with ESMTP id 4Z99Rz2sjtz11Q4; Sat, 8 Mar 2025 17:41:03 +0000 (UTC) Received: from [93.190.126.19] (mx1.investici.org [93.190.126.19]) (Authenticated sender: goodoldpaul@autistici.org) by localhost (Postfix) with ESMTPSA id 4Z99Rz1kwtz10xY; Sat, 8 Mar 2025 17:41:03 +0000 (UTC) Date: Sat, 8 Mar 2025 18:40:24 +0100 Message-ID: <2dd98bb474bf22628ba83cc316acaa1c0f0fbdf3.1741455624.git.goodoldpaul@autistici.org> X-Mailer: git-send-email 2.48.1 In-Reply-To: <3dbfa227b49e6f0cd7634d5d42fac38a549c0062.1741455624.git.goodoldpaul@autistici.org> References: <3dbfa227b49e6f0cd7634d5d42fac38a549c0062.1741455624.git.goodoldpaul@autistici.org> MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Giacomo Leidi X-ACL-Warn: , Giacomo Leidi via Guix-patches X-Patchwork-Original-From: Giacomo Leidi via Guix-patches via From: paul Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches * gnu/services/backup.scm (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 directories for timers logs. * gnu/home/services/backup.scm: New file. * gnu/local.mk: Add this. * doc/guix.texi: Document this. Change-Id: Ied1c0a5756b715fba176a0e42ea154246089e6be --- doc/guix.texi | 76 ++++++++++++++++++++++ gnu/home/services/backup.scm | 38 +++++++++++ gnu/local.mk | 1 + gnu/services/backup.scm | 119 +++++++++++++++++++++-------------- 4 files changed, 186 insertions(+), 48 deletions(-) create mode 100644 gnu/home/services/backup.scm diff --git a/doc/guix.texi b/doc/guix.texi index 321c312fa83..a56bfdddea7 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -463,6 +463,7 @@ Top * GPG: GNU Privacy Guard. Setting up GPG and related tools. * Desktop: Desktop Home Services. Services for graphical environments. * Guix: Guix Home Services. Services for Guix. +* Backup: Backup Home Services. Services for backing up User's files. * Fonts: Fonts Home Services. Services for managing User's fonts. * Sound: Sound Home Services. Dealing with audio. * Mail: Mail Home Services. Services for managing mail. @@ -47766,6 +47767,7 @@ Home Services * GPG: GNU Privacy Guard. Setting up GPG and related tools. * Desktop: Desktop Home Services. Services for graphical environments. * Guix: Guix Home Services. Services for Guix. +* Backup: Backup Home Services. Services for backing up User's files. * Fonts: Fonts Home Services. Services for managing User's fonts. * Sound: Sound Home Services. Dealing with audio. * Mail: Mail Home Services. Services for managing mail. @@ -49312,6 +49314,80 @@ Guix Home Services @end lisp @end defvar +@node Backup Home Services +@subsection Backup Services + +The @code{(gnu home services backup)} module offers services for backing up +file system trees. For now, it provides the @code{home-restic-backup-service-type}. + +With @code{home-restic-backup-service-type}, you can periodically back up +directories and files with @uref{https://restic.net/, Restic}, which +supports end-to-end encryption and deduplication. Consider the +following configuration: + +@lisp +(use-modules (gnu home services backup) ;for 'restic-backup-job', 'home-restic-backup-service-type' + (gnu packages sync) ;for 'rclone' + @dots{}) + +(home-environment + + (packages (list rclone ;for use by restic + @dots{})) + (services + (list + @dots{} + (simple-service 'backup-jobs + home-restic-backup-service-type + (list (restic-backup-job + (name "remote-ftp") + (repository "rclone:remote-ftp:backup/restic") + (password-file "/home/alice/.restic") + ;; Every day at 23. + (schedule "0 23 * * *") + (files '("/home/alice/.restic" + "/home/alice/.config/rclone" + "/home/alice/Pictures")))))))) +@end lisp + +In general it is preferrable to extend the @code{home-restic-backup-service-type}, +as shown in the example above. This is because it takes care of wrapping everything +with @code{for-home}, which enables the @code{home-restic-backup-service-type} and +@code{restic-backup-service-type} to share the same codebase. + +For a custom configuration, wrap your @code{restic-backup-configuration} in +@code{for-home}, as in this example: + +@lisp +(use-modules (gnu services) ;for 'for-home' + (gnu services backup) ;for 'restic-backup-job' and 'restic-backup-configuration' + (gnu home services backup) ;for 'home-restic-backup-service-type' + (gnu packages sync) ;for 'rclone' + @dots{}) + +(home-environment + + (packages (list rclone ;for use by restic + @dots{})) + (services + (list + @dots{} + (service home-restic-backup-service-type + (for-home + (restic-backup-configuration + (jobs (list @dots{})))))))) +@end lisp + +You can refer to @pxref{Miscellaneous Services, +@code{restic-backup-service-type}} for details about +@code{restic-backup-configuration} and @code{restic-backup-job}. +The only difference is that the @code{home-restic-backup-service-type} +will ignore the @code{user} and @code{group} field of +@code{restic-backup-job}. + +It will also install the @command{restic-guix} package to the user's Home +profile. + @node Fonts Home Services @subsection Fonts Home Services diff --git a/gnu/home/services/backup.scm b/gnu/home/services/backup.scm new file mode 100644 index 00000000000..ac977f835b6 --- /dev/null +++ b/gnu/home/services/backup.scm @@ -0,0 +1,38 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2025 Giacomo Leidi +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu home services backup) + #:use-module (gnu services) + #:use-module (gnu services backup) + #:use-module (gnu home services) + #:use-module (gnu home services shepherd) + #:export (home-restic-backup-service-type) + #:re-export (restic-backup-configuration + restic-backup-job)) + +(define home-restic-backup-service-type + (service-type + (inherit (system->home-service-type restic-backup-service-type)) + (extend + (lambda (config jobs) + (for-home + (restic-backup-configuration + (inherit config) + (jobs (append (restic-backup-configuration-jobs config) + jobs)))))) + (default-value (for-home (restic-backup-configuration))))) diff --git a/gnu/local.mk b/gnu/local.mk index be1e32571cf..5a0be62b97f 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -103,6 +103,7 @@ GNU_SYSTEM_MODULES = \ %D%/home.scm \ %D%/home/services.scm \ %D%/home/services/admin.scm \ + %D%/home/services/backup.scm \ %D%/home/services/desktop.scm \ %D%/home/services/dict.scm \ %D%/home/services/dotfiles.scm \ diff --git a/gnu/services/backup.scm b/gnu/services/backup.scm index dc095593432..3c092629774 100644 --- a/gnu/services/backup.scm +++ b/gnu/services/backup.scm @@ -28,6 +28,7 @@ (define-module (gnu services backup) #:prefix license:) #:use-module (guix modules) #:use-module (guix packages) + #:use-module (guix records) #:use-module (srfi srfi-1) #:export (restic-backup-job restic-backup-job? @@ -49,7 +50,6 @@ (define-module (gnu services backup) restic-backup-configuration restic-backup-configuration? - restic-backup-configuration-fields restic-backup-configuration-jobs restic-program @@ -57,7 +57,6 @@ (define-module (gnu services backup) restic-guix restic-guix-wrapper-package restic-backup-service-profile - restic-backup-service-activation restic-backup-service-type)) (define (gexp-or-string? value) @@ -132,13 +131,18 @@ (define-configuration/no-serialization restic-backup-job "A list of values that are lowered to strings. These will be passed as command-line arguments to the current @command{restic} invocation.")) -(define list-of-restic-backup-jobs? - (list-of restic-backup-job?)) +;; (for-home (restic-backup-configuration ...)) is not able to replace for-home? with #t, +;; pk prints #f. Once for-home will be able to work with (gnu services configuration) the +;; record can be migrated back to define-configuration. +(define-record-type* + restic-backup-configuration + make-restic-backup-configuration + restic-backup-configuration? + this-restic-backup-configuration -(define-configuration/no-serialization restic-backup-configuration - (jobs - (list-of-restic-backup-jobs '()) - "The list of backup jobs for the current system.")) + (jobs restic-backup-configuration-jobs (default '())) ; list of restic-backup-job + (home-service? restic-backup-configuration-home-service? + (default for-home?) (innate))) (define %restic-guix-supported-actions '("backup" "list" "ls" "mount" "prune" "restore" "snapshots" "unlock")) @@ -244,53 +248,79 @@ (define* (restic-guix config #:key (supported-actions (main (command-line))))) -(define (restic-job-log-file job) +(define* (restic-backup-job-log-file job #:key (home-service? #f)) (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")))) + (if home-service? + #~(begin + (use-modules (shepherd support)) + (string-append %user-log-dir "/restic-backup/" #$name ".log")) + (string-append "/var/log/restic-backup/" name ".log"))))) -(define (restic-backup-job->shepherd-service config) +(define* (restic-backup-job-command name files #:key (home-service? #f)) + (if home-service? + #~(list + "restic-guix" "backup" #$name #$@files) + #~(list + (string-append #+bash-minimal "/bin/bash") + "-l" "-c" + (string-append + "restic-guix backup " #$name " " + #$(string-join + (map (lambda (f) (string-append "'" f "'")) + files) + " "))))) + +(define* (restic-backup-job-requirement requirement #:key (home-service? #f)) + (if home-service? + requirement + (append '(user-processes file-systems) requirement))) + +(define* (restic-backup-job-modules #:key (home-service? #f)) + `((shepherd service timer) + ,@(if home-service? + ;;for %user-log-dir + '((shepherd support)) + '()))) + +(define* (restic-backup-job->shepherd-service config #:key (home-service? #f)) (let ((schedule (restic-backup-job-schedule config)) (name (restic-backup-job-name config)) - (files (string-join - (map (lambda (f) (string-append "'" f "'")) - (restic-backup-job-files config)) - " ")) + (files (restic-backup-job-files 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))) + (log-file (restic-backup-job-log-file + config #:home-service? home-service?)) + (requirement + (restic-backup-job-requirement + (restic-backup-job-requirement config) + #:home-service? home-service?))) (shepherd-service (provision `(,(string->symbol name))) - (requirement - `(user-processes file-systems ,@requirement)) + (requirement requirement) (documentation "Run @code{restic} backed backups on a regular basis.") - (modules '((shepherd service timer))) + (modules (restic-backup-job-modules + #:home-service? home-service?)) (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 " " #$files)) - #:user #$user - #:group #$group - #:environment-variables - (list - (string-append - "HOME=" (passwd:dir (getpwnam #$user))))) + #$(restic-backup-job-command + name files #:home-service? home-service?) + #$@(if home-service? '() (list #:user user)) + #$@(if home-service? '() (list #:group group)) + #$@(if home-service? '() + (list + #: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) @@ -329,26 +359,19 @@ (define restic-backup-service-profile (restic-guix-wrapper-package config)) '()))) -(define (restic-backup-service-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-service-activation) (service-extension profile-service-type restic-backup-service-profile) (service-extension shepherd-root-service-type - (lambda (config) - (map restic-backup-job->shepherd-service - (restic-backup-configuration-jobs - config)))))) + (match-record-lambda + (jobs home-service?) + (map (lambda (job) + (restic-backup-job->shepherd-service + job #:home-service? home-service?)) + jobs))))) (compose concatenate) (extend (lambda (config jobs)