about summary refs log tree commit diff
path: root/nixpkgs/nixos/lib
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-06-23 08:16:49 +0000
committerAlyssa Ross <hi@alyssa.is>2021-06-23 08:16:49 +0000
commit4dc730a187d41f9f2dce4fe2680c32dcecb79b93 (patch)
tree67673deb1649dc0a40962e70e6da302d7b6c4bf0 /nixpkgs/nixos/lib
parent633cab0ecb07627706c6b523e219490f019eaab5 (diff)
parent7e567a3d092b7de69cdf5deaeb8d9526de230916 (diff)
downloadnixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.tar
nixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.tar.gz
nixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.tar.bz2
nixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.tar.lz
nixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.tar.xz
nixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.tar.zst
nixlib-4dc730a187d41f9f2dce4fe2680c32dcecb79b93.zip
Merge commit '7e567a3d092b7de69cdf5deaeb8d9526de230916'
# Conflicts:
#	nixpkgs/pkgs/top-level/all-packages.nix
Diffstat (limited to 'nixpkgs/nixos/lib')
-rw-r--r--nixpkgs/nixos/lib/test-driver/test-driver.py2
-rw-r--r--nixpkgs/nixos/lib/testing-python.nix257
-rw-r--r--nixpkgs/nixos/lib/utils.nix26
3 files changed, 160 insertions, 125 deletions
diff --git a/nixpkgs/nixos/lib/test-driver/test-driver.py b/nixpkgs/nixos/lib/test-driver/test-driver.py
index 9c97ce383437..5be741395a08 100644
--- a/nixpkgs/nixos/lib/test-driver/test-driver.py
+++ b/nixpkgs/nixos/lib/test-driver/test-driver.py
@@ -973,7 +973,7 @@ def subtest(name: str) -> Iterator[None]:
 
 
 if __name__ == "__main__":
-    arg_parser = argparse.ArgumentParser()
+    arg_parser = argparse.ArgumentParser(prog="nixos-test-driver")
     arg_parser.add_argument(
         "-K",
         "--keep-vm-state",
diff --git a/nixpkgs/nixos/lib/testing-python.nix b/nixpkgs/nixos/lib/testing-python.nix
index 715482e87304..dbff6b98f0d6 100644
--- a/nixpkgs/nixos/lib/testing-python.nix
+++ b/nixpkgs/nixos/lib/testing-python.nix
@@ -16,13 +16,19 @@ rec {
 
   inherit pkgs;
 
-
-  mkTestDriver =
+  # Reifies and correctly wraps the python test driver for
+  # the respective qemu version and with or without ocr support
+  pythonTestDriver = {
+      qemu_pkg ? pkgs.qemu_test
+    , enableOCR ? false
+  }:
     let
-      testDriverScript = ./test-driver/test-driver.py;
-    in
-    qemu_pkg: stdenv.mkDerivation {
       name = "nixos-test-driver";
+      testDriverScript = ./test-driver/test-driver.py;
+      ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
+      imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
+    in stdenv.mkDerivation {
+      inherit name;
 
       nativeBuildInputs = [ makeWrapper ];
       buildInputs = [ (python3.withPackages (p: [ p.ptpython p.colorama ])) ];
@@ -35,7 +41,7 @@ rec {
       buildPhase = ''
         python <<EOF
         from pydoc import importfile
-        with open('driver-exports', 'w') as fp:
+        with open('driver-symbols', 'w') as fp:
           fp.write(','.join(dir(importfile('${testDriverScript}'))))
         EOF
       '';
@@ -57,20 +63,17 @@ rec {
           # TODO: copy user script part into this file (append)
 
           wrapProgram $out/bin/nixos-test-driver \
+            --argv0 ${name} \
             --prefix PATH : "${lib.makeBinPath [ qemu_pkg vde2 netpbm coreutils ]}" \
+            ${lib.optionalString enableOCR
+              "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
 
-          install -m 0644 -vD driver-exports $out/nix-support/driver-exports
+          install -m 0644 -vD driver-symbols $out/nix-support/driver-symbols
         '';
     };
 
   # Run an automated test suite in the given virtual network.
-  runTests = {
-    # the script that runs the network
-    driver,
-    # a source position in the format of builtins.unsafeGetAttrPos
-    # for meta.position
-    pos,
-  }:
+  runTests = { driver, pos }:
     stdenv.mkDerivation {
       name = "vm-test-run-${driver.testName}";
 
@@ -87,10 +90,99 @@ rec {
         inherit driver;
       };
 
-      inherit pos;
+      inherit pos; # for better debugging
     };
 
+  # Generate convenience wrappers for running the test driver
+  # has vlans, vms and test script defaulted through env variables
+  # also instantiates test script with nodes, if it's a function (contract)
+  setupDriverForTest = {
+      testScript
+    , testName
+    , nodes
+    , qemu_pkg ? pkgs.qemu_test
+    , enableOCR ? false
+    , skipLint ? false
+    , passthru ? {}
+  }:
+    let
+      # FIXME: get this pkg from the module system
+      testDriver = pythonTestDriver { inherit qemu_pkg enableOCR;};
+
+      testDriverName =
+        let
+          # A standard store path to the vm monitor is built like this:
+          #   /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
+          # The max filename length of a unix domain socket is 108 bytes.
+          # This means $name can at most be 50 bytes long.
+          maxTestNameLen = 50;
+          testNameLen = builtins.stringLength testName;
+        in with builtins;
+          if testNameLen > maxTestNameLen then
+            abort
+              ("The name of the test '${testName}' must not be longer than ${toString maxTestNameLen} " +
+                "it's currently ${toString testNameLen} characters long.")
+          else
+            "nixos-test-driver-${testName}";
+
+      vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
+      vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
+
+      nodeHostNames = map (c: c.config.system.name) (lib.attrValues nodes);
+
+      invalidNodeNames = lib.filter
+        (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null)
+        (builtins.attrNames nodes);
+
+      testScript' =
+        # Call the test script with the computed nodes.
+        if lib.isFunction testScript
+        then testScript { inherit nodes; }
+        else testScript;
 
+    in
+    if lib.length invalidNodeNames > 0 then
+      throw ''
+        Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
+        All machines are referenced as python variables in the testing framework which will break the
+        script when special characters are used.
+        Please stick to alphanumeric chars and underscores as separation.
+      ''
+    else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName
+      {
+        inherit testName;
+        nativeBuildInputs = [ makeWrapper ];
+        testScript = testScript';
+        preferLocalBuild = true;
+        passthru = passthru // {
+          inherit nodes;
+        };
+      }
+      ''
+        mkdir -p $out/bin
+
+        echo -n "$testScript" > $out/test-script
+        ${lib.optionalString (!skipLint) ''
+          PYFLAKES_BUILTINS="$(
+            echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)},
+            < ${lib.escapeShellArg "${testDriver}/nix-support/driver-symbols"}
+          )" ${python3Packages.pyflakes}/bin/pyflakes $out/test-script
+        ''}
+
+        ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
+        vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
+        wrapProgram $out/bin/nixos-test-driver \
+          --add-flags "''${vms[*]}" \
+          --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \
+          --set VLANS '${toString vlans}'
+        ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
+        wrapProgram $out/bin/nixos-run-vms \
+          --add-flags "''${vms[*]}" \
+          --set tests 'start_all(); join_all();' \
+          --set VLANS '${toString vlans}'
+      '');
+
+  # Make a full-blown test
   makeTest =
     { testScript
     , enableOCR ? false
@@ -106,128 +198,47 @@ rec {
     , ...
     } @ t:
     let
-      # A standard store path to the vm monitor is built like this:
-      #   /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
-      # The max filename length of a unix domain socket is 108 bytes.
-      # This means $name can at most be 50 bytes long.
-      maxTestNameLen = 50;
-      testNameLen = builtins.stringLength name;
-
-
-
-      ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
-
-      imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
-
-      # Generate convenience wrappers for running the test driver
-      # interactively with the specified network, and for starting the
-      # VMs from the command line.
-      mkDriver = qemu_pkg:
+      nodes = qemu_pkg:
         let
           build-vms = import ./build-vms.nix {
             inherit system pkgs minimal specialArgs;
-            extraConfigurations = extraConfigurations ++ (pkgs.lib.optional (qemu_pkg != null)
+            extraConfigurations = extraConfigurations ++ [(
               {
                 virtualisation.qemu.package = qemu_pkg;
-              }
-            ) ++ [(
-              {
                 # Ensure we do not use aliases. Ideally this is only set
                 # when the test framework is used by Nixpkgs NixOS tests.
                 nixpkgs.config.allowAliases = false;
               }
             )];
           };
-
-          # FIXME: get this pkg from the module system
-          testDriver = mkTestDriver (if qemu_pkg == null then pkgs.qemu_test else qemu_pkg);
-
-          nodes = build-vms.buildVirtualNetwork (
-            t.nodes or (if t ? machine then { machine = t.machine; } else { })
-          );
-          vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
-          vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
-
-          testScript' =
-            # Call the test script with the computed nodes.
-            if lib.isFunction testScript
-            then testScript { inherit nodes; }
-            else testScript;
-
-          testDriverName = with builtins;
-            if testNameLen > maxTestNameLen then
-              abort
-                ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
-                  "it's currently ${toString testNameLen} characters long.")
-            else
-              "nixos-test-driver-${name}";
         in
-        lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName
-          {
-            nativeBuildInputs = [ makeWrapper ];
-            testScript = testScript';
-            preferLocalBuild = true;
-            testName = name;
-            passthru = passthru // {
-              inherit nodes;
-            };
-          }
-          ''
-            mkdir -p $out/bin
-
-            echo -n "$testScript" > $out/test-script
-            ${lib.optionalString (!skipLint) ''
-              PYFLAKES_BUILTINS="$(
-                echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)},
-                < ${lib.escapeShellArg "${testDriver}/nix-support/driver-exports"}
-              )" ${python3Packages.pyflakes}/bin/pyflakes $out/test-script
-            ''}
-
-            ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
-            vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
-            wrapProgram $out/bin/nixos-test-driver \
-              --add-flags "''${vms[*]}" \
-              ${lib.optionalString enableOCR
-                "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
-              --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \
-              --set VLANS '${toString vlans}'
-            ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
-            wrapProgram $out/bin/nixos-run-vms \
-              --add-flags "''${vms[*]}" \
-              ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
-              --set tests 'start_all(); join_all();' \
-              --set VLANS '${toString vlans}'
-          ''); # "
-
-      passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
-        meta = (drv.meta or { }) // t.meta;
-      };
-
-      driver = mkDriver null;
-      driverInteractive = mkDriver pkgs.qemu;
-
-      test = passMeta (runTests { inherit driver pos; });
+          build-vms.buildVirtualNetwork (
+              t.nodes or (if t ? machine then { machine = t.machine; } else { })
+          );
 
-      nodeNames = builtins.attrNames driver.nodes;
-      invalidNodeNames = lib.filter
-        (node: builtins.match "^[A-z_]([A-z0-9_]+)?$" node == null)
-        nodeNames;
+      driver = setupDriverForTest {
+        inherit testScript enableOCR skipLint passthru;
+        testName = name;
+        qemu_pkg = pkgs.qemu_test;
+        nodes = nodes pkgs.qemu_test;
+      };
+      driverInteractive = setupDriverForTest {
+        inherit testScript enableOCR skipLint passthru;
+        testName = name;
+        qemu_pkg = pkgs.qemu;
+        nodes = nodes pkgs.qemu;
+      };
 
-      nodeHostNames = map (c: c.config.system.name) (lib.attrValues driver.nodes);
+      test =
+        let
+          passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
+            meta = (drv.meta or { }) // t.meta;
+          };
+        in passMeta (runTests { inherit driver pos; });
 
     in
-    if lib.length invalidNodeNames > 0 then
-      throw ''
-        Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
-        All machines are referenced as python variables in the testing framework which will break the
-        script when special characters are used.
-
-        Please stick to alphanumeric chars and underscores as separation.
-      ''
-    else
       test // {
-        inherit test driver driverInteractive;
-        inherit (driver) nodes;
+        inherit test driver driverInteractive nodes;
       };
 
   runInMachine =
@@ -235,7 +246,7 @@ rec {
     , machine
     , preBuild ? ""
     , postBuild ? ""
-    , qemu ? pkgs.qemu_test
+    , qemu_pkg ? pkgs.qemu_test
     , ... # ???
     }:
     let
@@ -272,6 +283,8 @@ rec {
         client.succeed("sync") # flush all data before pulling the plug
       '';
 
+      testDriver = pythonTestDriver { inherit qemu_pkg; };
+
       vmRunCommand = writeText "vm-run" ''
         xchg=vm-state-client/xchg
         ${coreutils}/bin/mkdir $out
@@ -290,7 +303,7 @@ rec {
         unset xchg
 
         export tests='${testScript}'
-        ${mkTestDriver qemu}/bin/nixos-test-driver --keep-vm-state ${vm.config.system.build.vm}/bin/run-*-vm
+        ${testDriver}/bin/nixos-test-driver --keep-vm-state ${vm.config.system.build.vm}/bin/run-*-vm
       ''; # */
 
     in
diff --git a/nixpkgs/nixos/lib/utils.nix b/nixpkgs/nixos/lib/utils.nix
index c9dfdbed99a3..f1332ab55930 100644
--- a/nixpkgs/nixos/lib/utils.nix
+++ b/nixpkgs/nixos/lib/utils.nix
@@ -14,8 +14,30 @@ rec {
   fsNeededForBoot = fs: fs.neededForBoot || elem fs.mountPoint pathsNeededForBoot;
 
   # Check whenever `b` depends on `a` as a fileSystem
-  fsBefore = a: b: a.mountPoint == b.device
-                || hasPrefix "${a.mountPoint}${optionalString (!(hasSuffix "/" a.mountPoint)) "/"}" b.mountPoint;
+  fsBefore = a: b:
+    let
+      # normalisePath adds a slash at the end of the path if it didn't already
+      # have one.
+      #
+      # The reason slashes are added at the end of each path is to prevent `b`
+      # from accidentally depending on `a` in cases like
+      #    a = { mountPoint = "/aaa"; ... }
+      #    b = { device     = "/aaaa"; ... }
+      # Here a.mountPoint *is* a prefix of b.device even though a.mountPoint is
+      # *not* a parent of b.device. If we add a slash at the end of each string,
+      # though, this is not a problem: "/aaa/" is not a prefix of "/aaaa/".
+      normalisePath = path: "${path}${optionalString (!(hasSuffix "/" path)) "/"}";
+      normalise = mount: mount // { device = normalisePath (toString mount.device);
+                                    mountPoint = normalisePath mount.mountPoint;
+                                    depends = map normalisePath mount.depends;
+                                  };
+
+      a' = normalise a;
+      b' = normalise b;
+
+    in hasPrefix a'.mountPoint b'.device
+    || hasPrefix a'.mountPoint b'.mountPoint
+    || any (hasPrefix a'.mountPoint) b'.depends;
 
   # Escape a path according to the systemd rules, e.g. /dev/xyzzy
   # becomes dev-xyzzy.  FIXME: slow.