services: unattended-upgrade: Rewrite as a Shepherd timer.

This is a semi-incompatible change: gexps previously provided in the
‘schedule’ field will no longer work.

* gnu/services/admin.scm (unattended-upgrade-mcron-jobs): Rename to…
(unattended-upgrade-shepherd-services): … this.  Return a list of one
Shepherd service.  Remove custom logging and time limitation facilities
from ‘code’.
(unattended-upgrade-service-type): Extend ‘shepherd-root-service-type’
instead of ‘mcron-service-type’.
(<unattended-upgrade-configuration>)[services-to-restart]: Change
default.
* doc/guix.texi (Unattended Upgrades): Adjust ‘schedule’ and
‘services-to-restart’ documentation.

Change-Id: I1b239c5946e71cf9e2af9b24fe4b01366b57fb7a
This commit is contained in:
Ludovic Courtès 2024-12-11 22:59:17 +01:00
parent 100c1e1adf
commit 48083c8c95
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
2 changed files with 45 additions and 37 deletions

View file

@ -23472,9 +23472,9 @@ service. The following fields are available:
@table @asis @table @asis
@item @code{schedule} (default: @code{"30 01 * * 0"}) @item @code{schedule} (default: @code{"30 01 * * 0"})
This is the schedule of upgrades, expressed as a gexp containing an This is the schedule of upgrades, expressed as a string in traditional
mcron job schedule (@pxref{Guile Syntax, mcron job specifications,, cron syntax or as a gexp evaluating to a Shepherd calendar event
mcron, GNU@tie{}mcron}). (@pxref{Timers,,, shepherd, The GNU Shepherd Manual}).
@item @code{channels} (default: @code{#~%default-channels}) @item @code{channels} (default: @code{#~%default-channels})
This gexp specifies the channels to use for the upgrade This gexp specifies the channels to use for the upgrade
@ -23523,7 +23523,7 @@ When @code{reboot?} is @code{#t}, services are not restarted before
rebooting. This means that the value for @code{services-to-restart} is rebooting. This means that the value for @code{services-to-restart} is
ignored. The updated services will be started after the system reboots. ignored. The updated services will be started after the system reboots.
@item @code{services-to-restart} (default: @code{'(mcron)}) @item @code{services-to-restart} (default: @code{'(unattended-upgrade)})
This field specifies the Shepherd services to restart when the upgrade This field specifies the Shepherd services to restart when the upgrade
completes. completes.
@ -23538,8 +23538,9 @@ Use @command{herd status} to find out candidates for restarting.
@xref{Services}, for general information about services. Common @xref{Services}, for general information about services. Common
services to restart would include @code{ntpd} and @code{ssh-daemon}. services to restart would include @code{ntpd} and @code{ssh-daemon}.
By default, the @code{mcron} service is restarted. This ensures that By default, the @code{unattended-upgrade} service is restarted. This
the latest version of the unattended upgrade job will be used next time. ensures that the latest version of the unattended upgrade job will be
used next time.
@item @code{system-expiration} (default: @code{(* 3 30 24 3600)}) @item @code{system-expiration} (default: @code{(* 3 30 24 3600)})
This is the expiration time in seconds for system generations. System This is the expiration time in seconds for system generations. System

View file

@ -553,7 +553,7 @@ which lets you search for packages that provide a given file.")
(reboot? unattended-upgrade-configuration-reboot? (reboot? unattended-upgrade-configuration-reboot?
(default #f)) (default #f))
(services-to-restart unattended-upgrade-configuration-services-to-restart (services-to-restart unattended-upgrade-configuration-services-to-restart
(default '(mcron))) (default '(unattended-upgrade)))
(system-expiration unattended-upgrade-system-expiration (system-expiration unattended-upgrade-system-expiration
(default (* 3 30 24 3600))) (default (* 3 30 24 3600)))
(maximum-duration unattended-upgrade-maximum-duration (maximum-duration unattended-upgrade-maximum-duration
@ -564,7 +564,7 @@ which lets you search for packages that provide a given file.")
(define %unattended-upgrade-log-file (define %unattended-upgrade-log-file
"/var/log/unattended-upgrade.log") "/var/log/unattended-upgrade.log")
(define (unattended-upgrade-mcron-jobs config) (define (unattended-upgrade-shepherd-services config)
(define channels (define channels
(scheme-file "channels.scm" (scheme-file "channels.scm"
(unattended-upgrade-configuration-channels config))) (unattended-upgrade-configuration-channels config)))
@ -572,6 +572,9 @@ which lets you search for packages that provide a given file.")
(define log (define log
(unattended-upgrade-configuration-log-file config)) (unattended-upgrade-configuration-log-file config))
(define schedule
(unattended-upgrade-configuration-schedule config))
(define services (define services
(unattended-upgrade-configuration-services-to-restart config)) (unattended-upgrade-configuration-services-to-restart config))
@ -598,35 +601,17 @@ which lets you search for packages that provide a given file.")
#~(begin #~(begin
(use-modules (guix build utils) (use-modules (guix build utils)
(gnu services herd) (gnu services herd)
(srfi srfi-19)
(srfi srfi-34)) (srfi srfi-34))
(define log (setvbuf (current-output-port) 'line)
(open-file #$log "a0")) (setvbuf (current-error-port) 'line)
(define (timestamp)
(date->string (time-utc->date (current-time time-utc))
"[~4]"))
(define (alarm-handler . _)
(format #t "~a time is up, aborting upgrade~%"
(timestamp))
(exit 1))
;; 'guix time-machine' needs X.509 certificates to authenticate the ;; 'guix time-machine' needs X.509 certificates to authenticate the
;; Git host. ;; Git host.
(setenv "SSL_CERT_DIR" (setenv "SSL_CERT_DIR"
#$(file-append nss-certs "/etc/ssl/certs")) #$(file-append nss-certs "/etc/ssl/certs"))
;; Make sure the upgrade doesn't take too long. (format #t "starting upgrade...~%")
(sigaction SIGALRM alarm-handler)
(alarm #$(unattended-upgrade-maximum-duration config))
;; Redirect stdout/stderr to LOG to save the output of 'guix' below.
(redirect-port log (current-output-port))
(redirect-port log (current-error-port))
(format #t "~a starting upgrade...~%" (timestamp))
(guard (c ((invoke-error? c) (guard (c ((invoke-error? c)
(report-invoke-error c))) (report-invoke-error c)))
(apply invoke #$(file-append guix "/bin/guix") (apply invoke #$(file-append guix "/bin/guix")
@ -645,23 +630,45 @@ which lets you search for packages that provide a given file.")
(unless #$reboot? (unless #$reboot?
;; Rebooting effectively restarts services anyway and execution ;; Rebooting effectively restarts services anyway and execution
;; would be halted here if mcron is restarted. ;; would be halted here if mcron is restarted.
(format #t "~a restarting services...~%" (timestamp)) (format #t "restarting services...~%")
(for-each restart-service '#$services)) (for-each restart-service '#$services))
;; XXX: If 'mcron' has been restarted, this is not reached. ;; XXX: If this service has been restarted, this is not reached.
(format #t "~a upgrade complete~%" (timestamp)) (format #t "upgrade complete~%")
;; Stopping the root shepherd service triggers a reboot. ;; Stopping the root shepherd service triggers a reboot.
(when #$reboot? (when #$reboot?
(format #t "~a rebooting system~%" (timestamp)) (format #t "rebooting system~%")
(force-output) ;ensure the entire log is written. (force-output) ;ensure the entire log is written.
(stop-service 'root)))))) (stop-service 'root))))))
(define upgrade (define upgrade
(program-file "unattended-upgrade" code)) (program-file "unattended-upgrade" code))
(list #~(job #$(unattended-upgrade-configuration-schedule config) (list (shepherd-service
#$upgrade))) (provision '(unattended-upgrade))
(requirement '(user-processes networking))
(modules '((shepherd service timer)))
(start #~(make-timer-constructor
#$(if (string? schedule)
#~(cron-string->calendar-event #$schedule)
schedule)
(command '(#$upgrade))
#:log-file #$log
;; Make sure the upgrade doesn't take too long.
#:max-duration
#$(unattended-upgrade-maximum-duration config)
;; Wait for the previous attempt to terminate before trying
;; again.
#:wait-for-termination? #t))
(stop #~(make-timer-destructor))
(actions (list (shepherd-action
(name 'trigger)
(documentation "Trigger unattended system upgrade.")
(procedure #~trigger-timer)))))))
(define (unattended-upgrade-log-rotations config) (define (unattended-upgrade-log-rotations config)
(list (log-rotation (list (log-rotation
@ -672,8 +679,8 @@ which lets you search for packages that provide a given file.")
(service-type (service-type
(name 'unattended-upgrade) (name 'unattended-upgrade)
(extensions (extensions
(list (service-extension mcron-service-type (list (service-extension shepherd-root-service-type
unattended-upgrade-mcron-jobs) unattended-upgrade-shepherd-services)
(service-extension rottlog-service-type (service-extension rottlog-service-type
unattended-upgrade-log-rotations))) unattended-upgrade-log-rotations)))
(description (description