diff options
author | Ryan Lahfa <masterancpp@gmail.com> | 2023-07-26 21:11:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-26 21:11:54 +0200 |
commit | 96f7ceb7b980489b79e9cc084bdb4b9481d421a6 (patch) | |
tree | fd12834fc6c310d0235b248d65f2a02d2e229f93 | |
parent | dac75c2caddb24565b14ebc41e6b654df9ec6cd1 (diff) | |
parent | e6862fae8fde54eba7e50367fd8a0ff7ebbfc1f3 (diff) |
Merge pull request #243995 from nikstur/systemd-sysupdate
-rw-r--r-- | nixos/doc/manual/release-notes/rl-2311.section.md | 1 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/system/boot/systemd/sysupdate.nix | 142 | ||||
-rw-r--r-- | nixos/tests/all-tests.nix | 1 | ||||
-rw-r--r-- | nixos/tests/common/gpg-keyring.nix | 21 | ||||
-rw-r--r-- | nixos/tests/systemd-nspawn.nix | 22 | ||||
-rw-r--r-- | nixos/tests/systemd-sysupdate.nix | 66 |
7 files changed, 233 insertions, 21 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index d8faa5e11dbe9..add72886e186c 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -34,6 +34,7 @@ - [ebusd](https://ebusd.eu), a daemon for handling communication with eBUS devices connected to a 2-wire bus system (“energy bus” used by numerous heating systems). Available as [services.ebusd](#opt-services.ebusd.enable). +- [systemd-sysupdate](https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html), atomically updates the host OS, container images, portable service images or other sources. Available as [systemd.sysupdate](opt-systemd.sysupdate). ## Backward Incompatibilities {#sec-release-23.11-incompatibilities} diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d1de6da182d2f..f7acbb59dc2c1 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1398,6 +1398,7 @@ ./system/boot/systemd/oomd.nix ./system/boot/systemd/repart.nix ./system/boot/systemd/shutdown.nix + ./system/boot/systemd/sysupdate.nix ./system/boot/systemd/tmpfiles.nix ./system/boot/systemd/user.nix ./system/boot/systemd/userdbd.nix diff --git a/nixos/modules/system/boot/systemd/sysupdate.nix b/nixos/modules/system/boot/systemd/sysupdate.nix new file mode 100644 index 0000000000000..2921e97f7560a --- /dev/null +++ b/nixos/modules/system/boot/systemd/sysupdate.nix @@ -0,0 +1,142 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.systemd.sysupdate; + + format = pkgs.formats.ini { }; + + listOfDefinitions = lib.mapAttrsToList + (name: format.generate "${name}.conf") + (lib.filterAttrs (k: _: !(lib.hasPrefix "_" k)) cfg.transfers); + + definitionsDirectory = pkgs.runCommand "sysupdate.d" { } '' + mkdir -p $out + ${(lib.concatStringsSep "\n" + (map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions) + )} + ''; +in +{ + options.systemd.sysupdate = { + + enable = lib.mkEnableOption (lib.mdDoc "systemd-sysupdate") // { + description = lib.mdDoc '' + Atomically update the host OS, container images, portable service + images or other sources. + + If enabled, updates are triggered in regular intervals via a + `systemd.timer` unit. + + Please see + <https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html> + for more details. + ''; + }; + + timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // { + default = { }; + description = lib.mdDoc '' + The timer configuration for performing the update. + + By default, the upstream configuration is used: + <https://github.com/systemd/systemd/blob/main/units/systemd-sysupdate.timer> + ''; + }; + + reboot = { + enable = lib.mkEnableOption (lib.mdDoc "automatically rebooting after an update") // { + description = lib.mdDoc '' + Whether to automatically reboot after an update. + + If set to `true`, the system will automatically reboot via a + `systemd.timer` unit but only after a new version was installed. + + This uses a unit completely separate from the one performing the + update because it is typically advisable to download updates + regularly while the system is up, but delay reboots until the + appropriate time (i.e. typically at night). + + Set this to `false` if you do not want to reboot after an update. This + is useful when you update a container image or another source where + rebooting is not necessary in order to finalize the update. + ''; + }; + + timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // { + default = { }; + description = lib.mdDoc '' + The timer configuration for rebooting after an update. + + By default, the upstream configuration is used: + <https://github.com/systemd/systemd/blob/main/units/systemd-sysupdate-reboot.timer> + ''; + }; + }; + + transfers = lib.mkOption { + type = with lib.types; attrsOf format.type; + default = { }; + example = { + "10-uki.conf" = { + Transfer = { + ProtectVersion = "%A"; + }; + + Source = { + Type = "url-file"; + Path = "https://download.example.com/"; + MatchPattern = "nixos_@v.efi.xz"; + }; + + Target = { + Type = "regular-file"; + Path = "/EFI/Linux"; + PathRelativeTo = "boot"; + MatchPattern = '' + nixos_@v+@l-@d.efi"; \ + nixos_@v+@l.efi \ + nixos_@v.efi + ''; + Mode = "0444"; + TriesLeft = 3; + TriesDone = 0; + InstancesMax = 2; + }; + }; + }; + description = lib.mdDoc '' + Specify transfers as a set of the names of the transfer files as the + key and the configuration as its value. The configuration can use all + upstream options. See + <https://www.freedesktop.org/software/systemd/man/sysupdate.d.html> + for all available options. + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + + systemd.additionalUpstreamSystemUnits = [ + "systemd-sysupdate.service" + "systemd-sysupdate.timer" + "systemd-sysupdate-reboot.service" + "systemd-sysupdate-reboot.timer" + ]; + + systemd.timers = { + "systemd-sysupdate" = { + wantedBy = [ "timers.target" ]; + timerConfig = cfg.timerConfig; + }; + "systemd-sysupdate-reboot" = lib.mkIf cfg.reboot.enable { + wantedBy = [ "timers.target" ]; + timerConfig = cfg.reboot.timerConfig; + }; + }; + + environment.etc."sysupdate.d".source = definitionsDirectory; + }; + + meta.maintainers = with lib.maintainers; [ nikstur ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 9ab0bfb21f4a9..c9ce2ebe91f33 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -772,6 +772,7 @@ in { systemd-portabled = handleTest ./systemd-portabled.nix {}; systemd-repart = handleTest ./systemd-repart.nix {}; systemd-shutdown = handleTest ./systemd-shutdown.nix {}; + systemd-sysupdate = runTest ./systemd-sysupdate.nix; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {}; systemd-misc = handleTest ./systemd-misc.nix {}; diff --git a/nixos/tests/common/gpg-keyring.nix b/nixos/tests/common/gpg-keyring.nix new file mode 100644 index 0000000000000..fb8d07b1183e0 --- /dev/null +++ b/nixos/tests/common/gpg-keyring.nix @@ -0,0 +1,21 @@ +{ pkgs, ... }: + +pkgs.runCommand "gpg-keyring" { nativeBuildInputs = [ pkgs.gnupg ]; } '' + mkdir -p $out + export GNUPGHOME=$out + cat > foo <<EOF + %echo Generating a basic OpenPGP key + %no-protection + Key-Type: EdDSA + Key-Curve: ed25519 + Name-Real: Bob Foobar + Name-Email: bob@foo.bar + Expire-Date: 0 + # Do a commit here, so that we can later print "done" + %commit + %echo done + EOF + gpg --batch --generate-key foo + rm $out/S.gpg-agent $out/S.gpg-agent.* + gpg --export bob@foo.bar -a > $out/pubkey.gpg +'' diff --git a/nixos/tests/systemd-nspawn.nix b/nixos/tests/systemd-nspawn.nix index bc77ee2a4d158..1a4251ef069e8 100644 --- a/nixos/tests/systemd-nspawn.nix +++ b/nixos/tests/systemd-nspawn.nix @@ -1,26 +1,6 @@ import ./make-test-python.nix ({pkgs, lib, ...}: let - gpgKeyring = (pkgs.runCommand "gpg-keyring" { buildInputs = [ pkgs.gnupg ]; } '' - mkdir -p $out - export GNUPGHOME=$out - cat > foo <<EOF - %echo Generating a basic OpenPGP key - %no-protection - Key-Type: DSA - Key-Length: 1024 - Subkey-Type: ELG-E - Subkey-Length: 1024 - Name-Real: Bob Foobar - Name-Email: bob@foo.bar - Expire-Date: 0 - # Do a commit here, so that we can later print "done" - %commit - %echo done - EOF - gpg --batch --generate-key foo - rm $out/S.gpg-agent $out/S.gpg-agent.* - gpg --export bob@foo.bar -a > $out/pubkey.gpg - ''); + gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; }; nspawnImages = (pkgs.runCommand "localhost" { buildInputs = [ pkgs.coreutils pkgs.gnupg ]; } '' mkdir -p $out diff --git a/nixos/tests/systemd-sysupdate.nix b/nixos/tests/systemd-sysupdate.nix new file mode 100644 index 0000000000000..37811605dbb2b --- /dev/null +++ b/nixos/tests/systemd-sysupdate.nix @@ -0,0 +1,66 @@ +# Tests downloading a signed update aritfact from a server to a target machine. +# This test does not rely on the `systemd.timer` units provided by the +# `systemd-sysupdate` module but triggers the `systemd-sysupdate` service +# manually to make the test more robust. + +{ lib, pkgs, ... }: + +let + gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; }; +in +{ + name = "systemd-sysupdate"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes = { + server = { pkgs, ... }: { + networking.firewall.enable = false; + services.nginx = { + enable = true; + virtualHosts."server" = { + root = pkgs.runCommand "sysupdate-artifacts" { buildInputs = [ pkgs.gnupg ]; } '' + mkdir -p $out + cd $out + + echo "nixos" > nixos_1.efi + sha256sum nixos_1.efi > SHA256SUMS + + export GNUPGHOME="$(mktemp -d)" + cp -R ${gpgKeyring}/* $GNUPGHOME + + gpg --batch --sign --detach-sign --output SHA256SUMS.gpg SHA256SUMS + ''; + }; + }; + }; + + target = { + systemd.sysupdate = { + enable = true; + transfers = { + "uki" = { + Source = { + Type = "url-file"; + Path = "http://server/"; + MatchPattern = "nixos_@v.efi"; + }; + Target = { + Path = "/boot/EFI/Linux"; + MatchPattern = "nixos_@v.efi"; + }; + }; + }; + }; + + environment.etc."systemd/import-pubring.gpg".source = "${gpgKeyring}/pubkey.gpg"; + }; + }; + + testScript = '' + server.wait_for_unit("nginx.service") + + target.succeed("systemctl start systemd-sysupdate") + assert "nixos" in target.wait_until_succeeds("cat /boot/EFI/Linux/nixos_1.efi", timeout=5) + ''; +} |