summary refs log tree commit diff
diff options
context:
space:
mode:
authorGraham Christensen <graham@grahamc.com>2020-03-05 17:07:20 -0500
committerGraham Christensen <graham@grahamc.com>2020-04-12 08:12:50 -0400
commitec2d28e3233c4c04fe6afe13e828691d099167ae (patch)
tree80d3efd6ffd191559bd5d222a765570cc73c604e
parent807ca93fadd5197c2260490de0c76e500562dc05 (diff)
downloadnixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.tar
nixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.tar.gz
nixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.tar.bz2
nixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.tar.lz
nixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.tar.xz
nixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.tar.zst
nixpkgs-ec2d28e3233c4c04fe6afe13e828691d099167ae.zip
specialisation: replace nesting with named configurations
Co-authored-by: worldofpeace <worldofpeace@protonmail.ch>
-rw-r--r--nixos/doc/manual/installation/installing-behind-a-proxy.xml2
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml44
-rw-r--r--nixos/modules/system/activation/no-clone.nix3
-rw-r--r--nixos/modules/system/activation/top-level.nix65
-rw-r--r--nixos/modules/system/boot/loader/grub/install-grub.pl5
-rw-r--r--nixos/modules/system/boot/loader/init-script/init-script-builder.sh2
-rw-r--r--nixos/tests/acme.nix94
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/caddy.nix44
-rw-r--r--nixos/tests/installer.nix20
-rw-r--r--nixos/tests/nesting.nix44
-rw-r--r--nixos/tests/nginx-etag.nix4
-rw-r--r--nixos/tests/nginx.nix53
-rw-r--r--nixos/tests/specialisation.nix43
14 files changed, 233 insertions, 192 deletions
diff --git a/nixos/doc/manual/installation/installing-behind-a-proxy.xml b/nixos/doc/manual/installation/installing-behind-a-proxy.xml
index 8f9baff44b5..c1ef638e876 100644
--- a/nixos/doc/manual/installation/installing-behind-a-proxy.xml
+++ b/nixos/doc/manual/installation/installing-behind-a-proxy.xml
@@ -40,7 +40,7 @@ networking.proxy.noProxy = &quot;127.0.0.1,localhost,internal.domain&quot;;
  <note>
   <para>
    If you are switching networks with different proxy configurations, use the
-   <literal>nesting.clone</literal> option in
+   <literal>specialisation</literal> option in
    <literal>configuration.nix</literal> to switch proxies at runtime. Refer to
    <xref linkend="ch-options" /> for more information.
   </para>
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index cf7946ffb80..4965f9c3629 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -203,6 +203,50 @@ environment.systemPackages = [
       <link xlink:href="https://github.com/gollum/gollum/wiki/5.0-release-notes#migrating-your-wiki">here</link>.
      </para>
    </listitem>
+
+   <listitem>
+    <para>
+      The NixOS options <literal>nesting.clone</literal> and
+      <literal>nesting.children</literal> have been deleted, and
+      replaced with named <xref linkend="opt-specialisation"/>
+      configurations.
+    </para>
+
+    <para>
+      Replace a <literal>nesting.clone</literal> entry with:
+
+<programlisting>{
+<link xlink:href="#opt-specialisation">specialisation.example-sub-configuration</link> = {
+  <link xlink:href="#opt-specialisation._name_.configuration">configuration</link> = {
+    ...
+  };
+};</programlisting>
+
+    </para>
+    <para>
+      Replace a <literal>nesting.children</literal> entry with:
+
+<programlisting>{
+<link xlink:href="#opt-specialisation">specialisation.example-sub-configuration</link> = {
+  <link xlink:href="#opt-specialisation._name_.inheritParentConfig">inheritParentConfig</link> = false;
+  <link xlink:href="#opt-specialisation._name_.configuration">configuration</link> = {
+    ...
+  };
+};</programlisting>
+    </para>
+
+    <para>
+     To switch to a specialised configuration at runtime you need to
+     run:
+<programlisting>
+# sudo /run/current-system/specialisation/example-sub-configuration/bin/switch-to-configuration test
+</programlisting>
+     Before you would have used:
+<programlisting>
+# sudo /run/current-system/fine-tune/child-1/bin/switch-to-configuration test
+</programlisting>
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 
diff --git a/nixos/modules/system/activation/no-clone.nix b/nixos/modules/system/activation/no-clone.nix
index 7f458443526..912420347dc 100644
--- a/nixos/modules/system/activation/no-clone.nix
+++ b/nixos/modules/system/activation/no-clone.nix
@@ -4,6 +4,5 @@ with lib;
 
 {
   boot.loader.grub.device = mkOverride 0 "nodev";
-  nesting.children = mkOverride 0 [];
-  nesting.clone = mkOverride 0 [];
+  specialisation = mkOverride 0 {};
 }
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 49693b6f1be..f6739977fa4 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -11,21 +11,16 @@ let
   # you can provide an easy way to boot the same configuration
   # as you use, but with another kernel
   # !!! fix this
-  cloner = inheritParent: list:
-    map (childConfig:
+  children = mapAttrs (childName: childConfig:
       (import ../../../lib/eval-config.nix {
         inherit baseModules;
         system = config.nixpkgs.initialSystem;
         modules =
-           (optionals inheritParent modules)
+           (optionals childConfig.inheritParentConfig modules)
         ++ [ ./no-clone.nix ]
-        ++ [ childConfig ];
+        ++ [ childConfig.configuration ];
       }).config.system.build.toplevel
-    ) list;
-
-  children =
-     cloner false config.nesting.children
-  ++ cloner true config.nesting.clone;
+    ) config.specialisation;
 
   systemBuilder =
     let
@@ -77,12 +72,9 @@ let
       echo -n "$nixosLabel" > $out/nixos-version
       echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
 
-      mkdir $out/fine-tune
-      childCount=0
-      for i in $children; do
-        childCount=$(( childCount + 1 ))
-        ln -s $i $out/fine-tune/child-$childCount
-      done
+      mkdir $out/specialisation
+      ${concatStringsSep "\n"
+      (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)}
 
       mkdir $out/bin
       export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
@@ -112,7 +104,6 @@ let
     shell = "${pkgs.bash}/bin/sh";
     su = "${pkgs.shadow.su}/bin/su";
 
-    inherit children;
     kernelParams = config.boot.kernelParams;
     installBootLoader =
       config.system.build.installBootLoader
@@ -143,6 +134,11 @@ let
 in
 
 {
+  imports = [
+    (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
+    (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
+  ];
+
   options = {
 
     system.build = mkOption {
@@ -154,26 +150,35 @@ in
       '';
     };
 
-    nesting.children = mkOption {
-      default = [];
-      description = ''
-        Additional configurations to build.
-      '';
-    };
-
-    nesting.clone = mkOption {
-      default = [];
+    specialisation = mkOption {
+      default = {};
+      example = lib.literalExample "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
       description = ''
-        Additional configurations to build based on the current
-        configuration which then has a lower priority.
+        Additional configurations to build. If
+        <literal>inheritParentConfig</literal> is true, the system
+        will be based on the overall system configuration.
 
-        To switch to a cloned configuration (e.g. <literal>child-1</literal>)
-        at runtime, run
+        To switch to a specialised configuration
+        (e.g. <literal>fewJobsManyCores</literal>) at runtime, run:
 
         <programlisting>
-        # sudo /run/current-system/fine-tune/child-1/bin/switch-to-configuration test
+        # sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test
         </programlisting>
       '';
+      type = types.attrsOf (types.submodule (
+        { ... }: {
+          options.inheritParentConfig = mkOption {
+            type = types.bool;
+            default = true;
+            description = "Include the entire system's configuration. Set to false to make a completely differently configured system.";
+          };
+
+          options.configuration = mkOption {
+            default = {};
+            description = "Arbitrary NixOS configuration options.";
+          };
+        })
+      );
     };
 
     system.boot.loader.id = mkOption {
diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl
index ca0fb0248e0..8df18cbd901 100644
--- a/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -409,7 +409,7 @@ $conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
 
 # Find all the children of the current default configuration
 # Do not search for grand children
-my @links = sort (glob "$defaultConfig/fine-tune/*");
+my @links = sort (glob "$defaultConfig/specialisation/*");
 foreach my $link (@links) {
 
     my $entryName = "";
@@ -425,7 +425,8 @@ foreach my $link (@links) {
     if ($cfgName) {
         $entryName = $cfgName;
     } else {
-        $entryName = "($date - $version)";
+        my $linkname = basename($link);
+        $entryName = "($linkname - $date - $version)";
     }
     addEntry("NixOS - $entryName", $link);
 }
diff --git a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
index 08d4ab14c9c..6f48d2539ac 100644
--- a/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
+++ b/nixos/modules/system/boot/loader/init-script/init-script-builder.sh
@@ -69,7 +69,7 @@ addEntry "NixOS - Default" $defaultConfig ""
 
 # Add all generations of the system profile to the menu, in reverse
 # (most recent to least recent) order.
-for link in $((ls -d $defaultConfig/fine-tune/* ) | sort -n); do
+for link in $((ls -d $defaultConfig/specialisation/* ) | sort -n); do
     date=$(stat --printf="%y\n" $link | sed 's/\..*//')
     addEntry "NixOS - variation" $link ""
 done
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix
index e045f3415fa..638601cd75f 100644
--- a/nixos/tests/acme.nix
+++ b/nixos/tests/acme.nix
@@ -91,52 +91,50 @@ in import ./make-test-python.nix {
 
       security.acme.server = "https://acme-v02.api.letsencrypt.org/dir";
 
-      nesting.clone = [
-        ({pkgs, ...}: {
-          systemd.targets."acme-finished-b.example.com" = {};
-          systemd.services."acme-b.example.com" = {
-            wants = [ "acme-finished-b.example.com.target" ];
-            before = [ "acme-finished-b.example.com.target" ];
-            after = [ "nginx.service" ];
-          };
-          services.nginx.virtualHosts."b.example.com" = {
-            enableACME = true;
-            forceSSL = true;
-            locations."/".root = pkgs.runCommand "docroot" {} ''
-              mkdir -p "$out"
-              echo hello world > "$out/index.html"
-            '';
-          };
-        })
-        ({pkgs, config, nodes, lib, ...}: {
-          security.acme.certs."example.com" = {
-            domain = "*.example.com";
-            dnsProvider = "exec";
-            dnsPropagationCheck = false;
-            credentialsFile = with pkgs; writeText "wildcard.env" ''
-              EXEC_PATH=${dnsScript { inherit writeScript bash curl; dnsAddress = nodes.dnsserver.config.networking.primaryIPAddress; }}
-            '';
-            user = config.services.nginx.user;
-            group = config.services.nginx.group;
-          };
-          systemd.targets."acme-finished-example.com" = {};
-          systemd.services."acme-example.com" = {
-            wants = [ "acme-finished-example.com.target" ];
-            before = [ "acme-finished-example.com.target" "nginx.service" ];
-            wantedBy = [ "nginx.service" ];
-          };
-          services.nginx.virtualHosts."c.example.com" = {
-            forceSSL = true;
-            sslCertificate = config.security.acme.certs."example.com".directory + "/cert.pem";
-            sslTrustedCertificate = config.security.acme.certs."example.com".directory + "/full.pem";
-            sslCertificateKey = config.security.acme.certs."example.com".directory + "/key.pem";
-            locations."/".root = pkgs.runCommand "docroot" {} ''
-              mkdir -p "$out"
-              echo hello world > "$out/index.html"
-            '';
-          };
-        })
-      ];
+      specialisation.second-cert.configuration = {pkgs, ...}: {
+        systemd.targets."acme-finished-b.example.com" = {};
+        systemd.services."acme-b.example.com" = {
+          wants = [ "acme-finished-b.example.com.target" ];
+          before = [ "acme-finished-b.example.com.target" ];
+          after = [ "nginx.service" ];
+        };
+        services.nginx.virtualHosts."b.example.com" = {
+          enableACME = true;
+          forceSSL = true;
+          locations."/".root = pkgs.runCommand "docroot" {} ''
+            mkdir -p "$out"
+            echo hello world > "$out/index.html"
+          '';
+        };
+      };
+      specialisation.dns-01.configuration = {pkgs, config, nodes, lib, ...}: {
+        security.acme.certs."example.com" = {
+          domain = "*.example.com";
+          dnsProvider = "exec";
+          dnsPropagationCheck = false;
+          credentialsFile = with pkgs; writeText "wildcard.env" ''
+            EXEC_PATH=${dnsScript { inherit writeScript bash curl; dnsAddress = nodes.dnsserver.config.networking.primaryIPAddress; }}
+          '';
+          user = config.services.nginx.user;
+          group = config.services.nginx.group;
+        };
+        systemd.targets."acme-finished-example.com" = {};
+        systemd.services."acme-example.com" = {
+          wants = [ "acme-finished-example.com.target" ];
+          before = [ "acme-finished-example.com.target" "nginx.service" ];
+          wantedBy = [ "nginx.service" ];
+        };
+        services.nginx.virtualHosts."c.example.com" = {
+          forceSSL = true;
+          sslCertificate = config.security.acme.certs."example.com".directory + "/cert.pem";
+          sslTrustedCertificate = config.security.acme.certs."example.com".directory + "/full.pem";
+          sslCertificateKey = config.security.acme.certs."example.com".directory + "/key.pem";
+          locations."/".root = pkgs.runCommand "docroot" {} ''
+            mkdir -p "$out"
+            echo hello world > "$out/index.html"
+          '';
+        };
+      };
     };
 
     client = {nodes, lib, ...}: {
@@ -196,7 +194,7 @@ in import ./make-test-python.nix {
 
       with subtest("Can add another certificate for nginx service"):
           webserver.succeed(
-              "/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
+              "/run/current-system/specialisation/second-cert/bin/switch-to-configuration test"
           )
           webserver.wait_for_unit("acme-finished-b.example.com.target")
           client.succeed(
@@ -208,7 +206,7 @@ in import ./make-test-python.nix {
               "${switchToNewServer}"
           )
           webserver.succeed(
-              "/run/current-system/fine-tune/child-2/bin/switch-to-configuration test"
+              "/run/current-system/specialisation/dns-01/bin/switch-to-configuration test"
           )
           webserver.wait_for_unit("acme-finished-example.com.target")
           client.succeed(
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 76ca4941617..5819879b30e 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -202,7 +202,7 @@ in
   nat.standalone = handleTest ./nat.nix { withFirewall = false; };
   ndppd = handleTest ./ndppd.nix {};
   neo4j = handleTest ./neo4j.nix {};
-  nesting = handleTest ./nesting.nix {};
+  specialisation = handleTest ./specialisation.nix {};
   netdata = handleTest ./netdata.nix {};
   networking.networkd = handleTest ./networking.nix { networkd = true; };
   networking.scripted = handleTest ./networking.nix { networkd = false; };
diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix
index fc10df0c79b..144d83179a1 100644
--- a/nixos/tests/caddy.nix
+++ b/nixos/tests/caddy.nix
@@ -20,35 +20,33 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         }
       '';
 
-      nesting.clone = [
-        {
-          services.caddy.config = lib.mkForce ''
-            http://localhost {
-              gzip
+      specialisation.etag.configuration = {
+        services.caddy.config = lib.mkForce ''
+          http://localhost {
+            gzip
 
-              root ${
-                pkgs.runCommand "testdir2" {} ''
-                  mkdir "$out"
-                  echo changed > "$out/example.html"
-                ''
-              }
+            root ${
+              pkgs.runCommand "testdir2" {} ''
+                mkdir "$out"
+                echo changed > "$out/example.html"
+              ''
             }
-          '';
-        }
+          }
+        '';
+      };
 
-        {
-          services.caddy.config = ''
-            http://localhost:8080 {
-            }
-          '';
-        }
-      ];
+      specialisation.config-reload.configuration = {
+        services.caddy.config = ''
+          http://localhost:8080 {
+          }
+        '';
+      };
     };
   };
 
   testScript = { nodes, ... }: let
-    etagSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-1";
-    justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-2";
+    etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag";
+    justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload";
   in ''
     url = "http://localhost/example.html"
     webserver.wait_for_unit("caddy")
@@ -77,7 +75,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         assert old_etag != new_etag, "Old ETag {} is the same as {}".format(
             old_etag, new_etag
         )
-    
+
     with subtest("config is reloaded on nixos-rebuild switch"):
         webserver.succeed(
             "${justReloadSystem}/bin/switch-to-configuration test >&2"
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index babde4126c4..c5abd458ec9 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -65,7 +65,7 @@ let
   # partitions and filesystems.
   testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi
                   , grubIdentifier, preBootCommands, extraConfig
-                  , testCloneConfig
+                  , testSpecialisationConfig
                   }:
     let iface = if grubVersion == 1 then "ide" else "virtio";
         isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
@@ -220,7 +220,7 @@ let
 
       # Tests for validating clone configuration entries in grub menu
     ''
-    + optionalString testCloneConfig ''
+    + optionalString testSpecialisationConfig ''
       # Reboot Machine
       machine = create_machine_named("clone-default-config")
       ${preBootCommands}
@@ -262,7 +262,7 @@ let
     , bootLoader ? "grub" # either "grub" or "systemd-boot"
     , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
     , enableOCR ? false, meta ? {}
-    , testCloneConfig ? false
+    , testSpecialisationConfig ? false
     }:
     makeTest {
       inherit enableOCR;
@@ -337,7 +337,7 @@ let
       testScript = testScriptFun {
         inherit bootLoader createPartitions preBootCommands
                 grubVersion grubDevice grubIdentifier grubUseEfi extraConfig
-                testCloneConfig;
+                testSpecialisationConfig;
       };
     };
 
@@ -411,11 +411,11 @@ let
     grubUseEfi = true;
   };
 
-  clone-test-extraconfig = {
+  specialisation-test-extraconfig = {
     extraConfig = ''
       environment.systemPackages = [ pkgs.grub2 ];
       boot.loader.grub.configurationName = "Home";
-      nesting.clone = [ {
+      specialisation.work.configuration = {
         boot.loader.grub.configurationName = lib.mkForce "Work";
 
         environment.etc = {
@@ -424,9 +424,9 @@ let
               gitproxy = none for work.com
               ";
         };
-      } ];
+      };
     '';
-    testCloneConfig = true;
+    testSpecialisationConfig = true;
   };
 
 
@@ -440,7 +440,7 @@ in {
   simple = makeInstallerTest "simple" simple-test-config;
 
   # Test cloned configurations with the simple grub configuration
-  simpleClone = makeInstallerTest "simpleClone" (simple-test-config // clone-test-extraconfig);
+  simpleSpecialised = makeInstallerTest "simpleSpecialised" (simple-test-config // specialisation-test-extraconfig);
 
   # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
   simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" {
@@ -467,7 +467,7 @@ in {
   simpleUefiGrub = makeInstallerTest "simpleUefiGrub" simple-uefi-grub-config;
 
   # Test cloned configurations with the uefi grub configuration
-  simpleUefiGrubClone = makeInstallerTest "simpleUefiGrubClone" (simple-uefi-grub-config // clone-test-extraconfig);
+  simpleUefiGrubSpecialisation = makeInstallerTest "simpleUefiGrubSpecialisation" (simple-uefi-grub-config // specialisation-test-extraconfig);
 
   # Same as the previous, but now with a separate /boot partition.
   separateBoot = makeInstallerTest "separateBoot" {
diff --git a/nixos/tests/nesting.nix b/nixos/tests/nesting.nix
deleted file mode 100644
index a75806b24ff..00000000000
--- a/nixos/tests/nesting.nix
+++ /dev/null
@@ -1,44 +0,0 @@
-import ./make-test-python.nix {
-  name = "nesting";
-  nodes =  {
-    clone = { pkgs, ... }: {
-      environment.systemPackages = [ pkgs.cowsay ];
-      nesting.clone = [
-        ({ pkgs, ... }: {
-          environment.systemPackages = [ pkgs.hello ];
-        })
-      ];
-    };
-    children = { pkgs, ... }: {
-      environment.systemPackages = [ pkgs.cowsay ];
-      nesting.children = [
-        ({ pkgs, ... }: {
-          environment.systemPackages = [ pkgs.hello ];
-        })
-      ];
-    };
-  };
-  testScript = ''
-    clone.wait_for_unit("default.target")
-    clone.succeed("cowsay hey")
-    clone.fail("hello")
-
-    with subtest("Nested clones do inherit from parent"):
-        clone.succeed(
-            "/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
-        )
-        clone.succeed("cowsay hey")
-        clone.succeed("hello")
-
-    children.wait_for_unit("default.target")
-    children.succeed("cowsay hey")
-    children.fail("hello")
-
-    with subtest("Nested children do not inherit from parent"):
-        children.succeed(
-            "/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"
-        )
-        children.fail("cowsay hey")
-        children.succeed("hello")
-  '';
-}
diff --git a/nixos/tests/nginx-etag.nix b/nixos/tests/nginx-etag.nix
index e357309d166..63ab2e0c6c2 100644
--- a/nixos/tests/nginx-etag.nix
+++ b/nixos/tests/nginx-etag.nix
@@ -19,7 +19,7 @@ import ./make-test-python.nix {
         '';
       };
 
-      nesting.clone = lib.singleton {
+      specialisation.pass-checks.configuration = {
         services.nginx.virtualHosts.server = {
           root = lib.mkForce (pkgs.runCommandLocal "testdir2" {} ''
             mkdir "$out"
@@ -70,7 +70,7 @@ import ./make-test-python.nix {
 
   testScript = { nodes, ... }: let
     inherit (nodes.server.config.system.build) toplevel;
-    newSystem = "${toplevel}/fine-tune/child-1";
+    newSystem = "${toplevel}/specialisation/pass-checks";
   in ''
     start_all()
 
diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix
index 7358800a676..18822f09568 100644
--- a/nixos/tests/nginx.nix
+++ b/nixos/tests/nginx.nix
@@ -42,38 +42,35 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
       services.nginx.enableReload = true;
 
-      nesting.clone = [
-        {
-          services.nginx.virtualHosts.localhost = {
-            root = lib.mkForce (pkgs.runCommand "testdir2" {} ''
-              mkdir "$out"
-              echo content changed > "$out/index.html"
-            '');
-          };
-        }
-
-        {
-          services.nginx.virtualHosts."1.my.test".listen = [ { addr = "127.0.0.1"; port = 8080; }];
-        }
-
-        {
-          services.nginx.package = pkgs.nginxUnstable;
-        }
-
-        {
-          services.nginx.package = pkgs.nginxUnstable;
-          services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
-        }
-      ];
-    };
+      specialisation.etagSystem.configuration = {
+        services.nginx.virtualHosts.localhost = {
+          root = lib.mkForce (pkgs.runCommand "testdir2" {} ''
+            mkdir "$out"
+            echo content changed > "$out/index.html"
+          '');
+        };
+      };
+
+      specialisation.justReloadSystem.configuration = {
+        services.nginx.virtualHosts."1.my.test".listen = [ { addr = "127.0.0.1"; port = 8080; }];
+      };
 
+      specialisation.reloadRestartSystem.configuration = {
+        services.nginx.package = pkgs.nginxUnstable;
+      };
+
+      specialisation.reloadWithErrorsSystem.configuration = {
+        services.nginx.package = pkgs.nginxUnstable;
+        services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
+      };
+    };
   };
 
   testScript = { nodes, ... }: let
-    etagSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-1";
-    justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-2";
-    reloadRestartSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-3";
-    reloadWithErrorsSystem = "${nodes.webserver.config.system.build.toplevel}/fine-tune/child-4";
+    etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etagSystem";
+    justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/justReloadSystem";
+    reloadRestartSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/reloadRestartSystem";
+    reloadWithErrorsSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/reloadWithErrorsSystem";
   in ''
     url = "http://localhost/index.html"
 
diff --git a/nixos/tests/specialisation.nix b/nixos/tests/specialisation.nix
new file mode 100644
index 00000000000..b8d4b8279f4
--- /dev/null
+++ b/nixos/tests/specialisation.nix
@@ -0,0 +1,43 @@
+import ./make-test-python.nix {
+  name = "specialisation";
+  nodes =  {
+    inheritconf = { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.cowsay ];
+      specialisation.inheritconf.configuration = { pkgs, ... }: {
+        environment.systemPackages = [ pkgs.hello ];
+      };
+    };
+    noinheritconf = { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.cowsay ];
+      specialisation.noinheritconf = {
+        inheritParentConfig = false;
+        configuration = { pkgs, ... }: {
+          environment.systemPackages = [ pkgs.hello ];
+        };
+      };
+    };
+  };
+  testScript = ''
+    inheritconf.wait_for_unit("default.target")
+    inheritconf.succeed("cowsay hey")
+    inheritconf.fail("hello")
+
+    with subtest("Nested clones do inherit from parent"):
+        inheritconf.succeed(
+            "/run/current-system/specialisation/inheritconf/bin/switch-to-configuration test"
+        )
+        inheritconf.succeed("cowsay hey")
+        inheritconf.succeed("hello")
+
+        noinheritconf.wait_for_unit("default.target")
+        noinheritconf.succeed("cowsay hey")
+        noinheritconf.fail("hello")
+
+    with subtest("Nested children do not inherit from parent"):
+        noinheritconf.succeed(
+            "/run/current-system/specialisation/noinheritconf/bin/switch-to-configuration test"
+        )
+        noinheritconf.fail("cowsay hey")
+        noinheritconf.succeed("hello")
+  '';
+}