services: Add 'unattended-upgrade-service-type'.

* gnu/services/admin.scm (<unattended-upgrade-configuration>): New
record type.
(%unattended-upgrade-log-file): New variable.
(unattended-upgrade-mcron-jobs, unattended-upgrade-log-rotations): New
procedures.
(unattended-upgrade-service-type): New variable.
* doc/guix.texi (Service Reference): Add 'provenance-service-type' anchor.
(Unattended Upgrades): New section.
This commit is contained in:
Ludovic Courtès 2020-07-22 20:21:21 +02:00
parent c20e697ea1
commit 79501f26ab
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
2 changed files with 251 additions and 2 deletions

View file

@ -12926,6 +12926,7 @@ declaration.
* Scheduled Job Execution:: The mcron service.
* Log Rotation:: The rottlog service.
* Networking Services:: Network setup, SSH daemon, etc.
* Unattended Upgrades:: Automated system upgrades.
* X Window:: Graphical display.
* Printing Services:: Local and remote printer support.
* Desktop Services:: D-Bus and desktop services.
@ -15298,6 +15299,117 @@ Use this to add additional options and manage shared secrets out-of-band.
@end table
@end deftp
@node Unattended Upgrades
@subsection Unattended Upgrades
@cindex unattended upgrades
@cindex upgrades, unattended
Guix provides a service to perform @emph{unattended upgrades}:
periodically, the system automatically reconfigures itself from the
latest Guix. Guix System has several properties that make unattended
upgrades safe:
@itemize
@item
upgrades are transactional (either the upgrade succeeds or it fails, but
you cannot end up with an ``in-between'' system state);
@item
the upgrade log is kept---you can view it with @command{guix system
list-generations}---and you can roll back to any previous generation,
should the upgraded system fail to behave as intended;
@item
channel code is authenticated so you know you can only run genuine code
(@pxref{Channels});
@item
@command{guix system reconfigure} prevents downgrades, which makes it
immune to @dfn{downgrade attacks}.
@end itemize
To set up unattended upgrades, add an instance of
@code{unattended-upgrade-service-type} like the one below to the list of
your operating system services:
@lisp
(service unattended-upgrade-service-type)
@end lisp
The defaults above set up weekly upgrades: every Sunday at midnight.
You do not need to provide the operating system configuration file: it
uses @file{/run/current-system/configuration.scm}, which ensures it
always uses your latest configuration---@pxref{provenance-service-type},
for more information about this file.
There are several things that can be configured, in particular the
periodicity and services (daemons) to be restarted upon completion.
When the upgrade is successful, the service takes care of deleting
system generations older that some threshold, as per @command{guix
system delete-generations}. See the reference below for details.
To ensure that upgrades are actually happening, you can run
@command{guix system describe}. To investigate upgrade failures, visit
the unattended upgrade log file (see below).
@defvr {Scheme Variable} unattended-upgrade-service-type
This is the service type for unattended upgrades. It sets up an mcron
job (@pxref{Scheduled Job Execution}) that runs @command{guix system
reconfigure} from the latest version of the specified channels.
Its value must be a @code{unattended-upgrade-configuration} record (see
below).
@end defvr
@deftp {Data Type} unattended-upgrade-configuration
This data type represents the configuration of the unattended upgrade
service. The following fields are available:
@table @asis
@item @code{schedule} (default: @code{"30 01 * * 0"})
This is the schedule of upgrades, expressed as a gexp containing an
mcron job schedule (@pxref{Guile Syntax, mcron job specifications,,
mcron, GNU@tie{}mcron}).
@item @code{channels} (default: @code{#~%default-channels})
This gexp specifies the channels to use for the upgrade
(@pxref{Channels}). By default, the tip of the official @code{guix}
channel is used.
@item @code{services-to-restart} (default: @code{'(mcron)})
This field specifies the Shepherd services to restart when the upgrade
completes.
Those services are restarted right away upon completion, as with
@command{herd restart}, which ensures that the latest version is
running---remember that by default @command{guix system reconfigure}
only restarts services that are not currently running, which is
conservative: it minimizes disruption but leaves outdated services
running.
By default, the @code{mcron} service is restarted. This 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)})
This is the expiration time in seconds for system generations. System
generations older that this amount of time are deleted with
@command{guix system delete-generations} when an upgrade completes.
@quotation Note
The unattended upgrade service does not run the garbage collector. You
will probably want to set up your own mcron job to run @command{guix gc}
periodically.
@end quotation
@item @code{maximum-duration} (default: @code{3600})
Maximum duration in seconds for the upgrade; past that time, the upgrade
aborts.
This is primarily useful to ensure the upgrade does not end up
rebuilding or re-downloading ``the world''.
@item @code{log-file} (default: @code{"/var/log/unattended-upgrade.log"})
File where unattended upgrades are logged.
@end table
@end deftp
@node X Window
@subsection X Window
@ -29628,6 +29740,7 @@ extend it by passing it lists of packages to add to the system profile.
@end defvr
@cindex provenance tracking, of the operating system
@anchor{provenance-service-type}
@defvr {Scheme Variable} provenance-service-type
This is the type of the service that records @dfn{provenance meta-data}
in the system itself. It creates several files under

View file

@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016 Jan Nieuwenhuizen <janneke@gnu.org>
;;; Copyright © 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re>
;;;
;;; This file is part of GNU Guix.
@ -20,10 +20,13 @@
(define-module (gnu services admin)
#:use-module (gnu packages admin)
#:use-module (gnu packages certs)
#:use-module (gnu packages package-management)
#:use-module (gnu services)
#:use-module (gnu services mcron)
#:use-module (gnu services shepherd)
#:use-module (guix gexp)
#:use-module (guix modules)
#:use-module (guix packages)
#:use-module (guix records)
#:use-module (srfi srfi-1)
@ -41,7 +44,17 @@ (define-module (gnu services admin)
rottlog-configuration
rottlog-configuration?
rottlog-service
rottlog-service-type))
rottlog-service-type
unattended-upgrade-service-type
unattended-upgrade-configuration
unattended-upgrade-configuration?
unattended-upgrade-configuration-channels
unattended-upgrade-configuration-schedule
unattended-upgrade-configuration-services-to-restart
unattended-upgrade-configuration-system-expiration
unattended-upgrade-configuration-maximum-duration
unattended-upgrade-configuration-log-file))
;;; Commentary:
;;;
@ -177,4 +190,127 @@ (define rottlog-service-type
rotations)))))
(default-value (rottlog-configuration))))
;;;
;;; Unattended upgrade.
;;;
(define-record-type* <unattended-upgrade-configuration>
unattended-upgrade-configuration make-unattended-upgrade-configuration
unattended-upgrade-configuration?
(schedule unattended-upgrade-configuration-schedule
(default "30 01 * * 0"))
(channels unattended-upgrade-configuration-channels
(default #~%default-channels))
(services-to-restart unattended-upgrade-configuration-services-to-restart
(default '(mcron)))
(system-expiration unattended-upgrade-system-expiration
(default (* 3 30 24 3600)))
(maximum-duration unattended-upgrade-maximum-duration
(default 3600))
(log-file unattended-upgrade-configuration-log-file
(default %unattended-upgrade-log-file)))
(define %unattended-upgrade-log-file
"/var/log/unattended-upgrade.log")
(define (unattended-upgrade-mcron-jobs config)
(define channels
(scheme-file "channels.scm"
(unattended-upgrade-configuration-channels config)))
(define log
(unattended-upgrade-configuration-log-file config))
(define services
(unattended-upgrade-configuration-services-to-restart config))
(define expiration
(unattended-upgrade-system-expiration config))
(define code
(with-imported-modules (source-module-closure '((guix build utils)
(gnu services herd)))
#~(begin
(use-modules (guix build utils)
(gnu services herd)
(srfi srfi-19)
(srfi srfi-34))
(define log
(open-file #$log "a0"))
(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))
(define-syntax-rule (with-logging exp ...)
(with-output-to-port log
(lambda ()
(with-error-to-port log
(lambda ()
exp ...)))))
;; 'guix time-machine' needs X.509 certificates to authenticate the
;; Git host.
(setenv "SSL_CERT_DIR"
#$(file-append nss-certs "/etc/ssl/certs"))
;; Make sure the upgrade doesn't take too long.
(sigaction SIGALRM alarm-handler)
(alarm #$(unattended-upgrade-maximum-duration config))
(with-logging
(format #t "~a starting upgrade...~%" (timestamp))
(guard (c ((invoke-error? c)
(report-invoke-error c)))
(invoke #$(file-append guix "/bin/guix")
"time-machine" "-C" #$channels
"--" "system" "reconfigure"
"/run/current-system/configuration.scm")
;; 'guix system delete-generations' fails when there's no
;; matching generation. Thus, catch 'invoke-error?'.
(guard (c ((invoke-error? c)
(report-invoke-error c)))
(invoke #$(file-append guix "/bin/guix")
"system" "delete-generations"
#$(string-append (number->string expiration)
"s")))
(format #t "~a restarting services...~%" (timestamp))
(for-each restart-service '#$services)
;; XXX: If 'mcron' has been restarted, perhaps this isn't
;; reached.
(format #t "~a upgrade complete~%" (timestamp)))))))
(define upgrade
(program-file "unattended-upgrade" code))
(list #~(job #$(unattended-upgrade-configuration-schedule config)
#$upgrade)))
(define (unattended-upgrade-log-rotations config)
(list (log-rotation
(files
(list (unattended-upgrade-configuration-log-file config))))))
(define unattended-upgrade-service-type
(service-type
(name 'unattended-upgrade)
(extensions
(list (service-extension mcron-service-type
unattended-upgrade-mcron-jobs)
(service-extension rottlog-service-type
unattended-upgrade-log-rotations)))
(description
"Periodically upgrade the system from the current configuration.")
(default-value (unattended-upgrade-configuration))))
;;; admin.scm ends here