From e3b90b6ccc4cf4147fd01df1ed8779b1c85744bd Mon Sep 17 00:00:00 2001
From: "Adrian Parvin D. Ouano" <me@adrianpar.vin>
Date: Tue, 9 Mar 2021 23:04:58 +0800
Subject: [PATCH] nixos/systemd: Handle template overrides

Adding template overrides allows for custom behavior for specific
instances of a template. Previously, it was not possible to provide
bind mounts for systemd-nspawn. This change allows it.
---
 nixos/modules/system/boot/systemd-lib.nix | 13 ++++++-
 nixos/tests/systemd-template-override.nix | 41 +++++++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)
 create mode 100644 nixos/tests/systemd-template-override.nix

diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix
index 2dbf15031a0..6051a428574 100644
--- a/nixos/modules/system/boot/systemd-lib.nix
+++ b/nixos/modules/system/boot/systemd-lib.nix
@@ -182,7 +182,18 @@ in rec {
       # upstream unit.
       for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do
         fn=$(basename $i/*)
-        if [ -e $out/$fn ]; then
+
+        case $fn in
+          # if file name is a template specialization, use the template's name
+          *@?*.service)
+            # remove @foo.service and replace it with @.service
+            ofn="''${fn%@*.service}@.service"
+            ;;
+          *)
+            ofn="$fn"
+        esac
+
+        if [ -e $out/$ofn ]; then
           if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
             ln -sfn /dev/null $out/$fn
           else
diff --git a/nixos/tests/systemd-template-override.nix b/nixos/tests/systemd-template-override.nix
new file mode 100644
index 00000000000..d8ef4a6c1c9
--- /dev/null
+++ b/nixos/tests/systemd-template-override.nix
@@ -0,0 +1,41 @@
+import ./make-test-python.nix {
+  name = "systemd-template-override";
+
+  machine = { pkgs, lib, ... }: let
+    touchTmp = pkgs.writeTextFile {
+      name = "touch-tmp@.service";
+      text = ''
+        [Service]
+        Type=oneshot
+        ExecStart=${pkgs.coreutils}/bin/touch /tmp/%I
+      '';
+      destination = "/etc/systemd/system/touch-tmp@.service";
+    };
+  in {
+    systemd.packages = [ touchTmp ];
+
+    systemd.services."touch-tmp@forbidden" = {
+      serviceConfig.ExecStart = [ "" ''
+        ${pkgs.coreutils}/bin/true
+      ''];
+    };
+
+    systemd.services."touch-tmp@intercept" = {
+      serviceConfig.ExecStart = [ "" ''
+        ${pkgs.coreutils}/bin/touch /tmp/renamed
+      ''];
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("default.target")
+
+    machine.succeed("systemctl start touch-tmp@normal")
+    machine.succeed("systemctl start touch-tmp@forbbidden")
+    machine.succeed("systemctl start touch-tmp@intercept")
+
+    machine.succeed("[ -e /tmp/normal ]")
+    machine.succeed("[ ! -e /tmp/forbidden ]")
+    machine.succeed("[ -e /tmp/renamed ]")
+  '';
+}