From b7fd022677881ef364859c1c1a54552a470c1f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac=20Jacqu=C3=A9?= Date: Thu, 6 Jul 2023 13:14:48 +0200 Subject: [PATCH] nixos/oci-containers: make systemd service pre-start extensible We were setting the systemd pre-start script through the systemd.services..preStart NixOS option. This option uses a string containing the pre-start script as input. In some scenarios, you want to extend this script to perform some additional actions before launching a container. At the moment, your only option is to mkForce the pre-start string and rewrite a preStart script from scratch. Potentially vendoring the Nixpkgs pre-start script in your custom pre-start script. (you can also create a new service unit in charge of running the custom pre-start and create a dependency link between the units, but that's also sub-optimal). The systemd.services..serviceConfig.ExecStartPre NixOS option gives us a better way to extend a pre-start script. Instead of being a simple script, this option can be a list of scripts. The NixOS module system then merges the multiple list declarations instead of overriding them. Meaning that if we use this ExecStartPre option, we can trivially extend the exec-start script: just add the custom script in the systemd service override and you're done. ExecStartPre behaves a tiny bit differently from preStart. Instead of expecting a string containing a script, it expects a path pointing to a script. We take advantage of this API change to check the pre-start script with shellCheck via the pkgs.writeShellApplication function. --- .../modules/virtualisation/oci-containers.nix | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix index a9f4ab77f866..402adef7d81a 100644 --- a/nixos/modules/virtualisation/oci-containers.nix +++ b/nixos/modules/virtualisation/oci-containers.nix @@ -228,6 +228,26 @@ let mkService = name: container: let dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn; escapedName = escapeShellArg name; + preStartScript = pkgs.writeShellApplication { + name = "pre-start"; + runtimeInputs = [ ]; + text = '' + ${cfg.backend} rm -f ${name} || true + ${optionalString (isValidLogin container.login) '' + cat ${container.login.passwordFile} | \ + ${cfg.backend} login \ + ${container.login.registry} \ + --username ${container.login.username} \ + --password-stdin + ''} + ${optionalString (container.imageFile != null) '' + ${cfg.backend} load -i ${container.imageFile} + ''} + ${optionalString (cfg.backend == "podman") '' + rm -f /run/podman-${escapedName}.ctr-id + ''} + ''; + }; in { wantedBy = [] ++ optional (container.autoStart) "multi-user.target"; after = lib.optionals (cfg.backend == "docker") [ "docker.service" "docker.socket" ] @@ -242,23 +262,6 @@ let else if cfg.backend == "podman" then [ config.virtualisation.podman.package ] else throw "Unhandled backend: ${cfg.backend}"; - preStart = '' - ${cfg.backend} rm -f ${name} || true - ${optionalString (isValidLogin container.login) '' - cat ${container.login.passwordFile} | \ - ${cfg.backend} login \ - ${container.login.registry} \ - --username ${container.login.username} \ - --password-stdin - ''} - ${optionalString (container.imageFile != null) '' - ${cfg.backend} load -i ${container.imageFile} - ''} - ${optionalString (cfg.backend == "podman") '' - rm -f /run/podman-${escapedName}.ctr-id - ''} - ''; - script = concatStringsSep " \\\n " ([ "exec ${cfg.backend} run" "--rm" @@ -306,7 +309,7 @@ let ### # ExecReload = ...; ### - + ExecStartPre = [ "${preStartScript}/bin/pre-start" ]; TimeoutStartSec = 0; TimeoutStopSec = 120; Restart = "always";