From 75c8541a8d7ad0cbd080cb431a5eb7a8424c56b0 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 15 Apr 2025 18:06:42 +0200 Subject: [PATCH] tests(clan): move test-inventory.nix to clanLib. And name it 'makeTestClan' Exposing the function via clanLib makes it more accessible to clan modules This allows each module to define its own test without needing to depend on any fileSystem path assumptions in the clan-core repo --- checks/data-mesher/default.nix | 134 +++++++++++++----------- checks/dummy-inventory-test/default.nix | 119 +++++++++++---------- checks/flake-module.nix | 3 +- checks/installation/flake-module.nix | 2 +- checks/lib/container-test.nix | 2 +- checks/lib/test-base.nix | 2 +- checks/lib/test-inventory.nix | 89 ---------------- lib/default.nix | 1 + lib/tests/default.nix | 92 ++++++++++++++++ {checks/lib => lib/tests}/minify.nix | 3 + 10 files changed, 236 insertions(+), 211 deletions(-) delete mode 100644 checks/lib/test-inventory.nix create mode 100644 lib/tests/default.nix rename {checks/lib => lib/tests}/minify.nix (62%) diff --git a/checks/data-mesher/default.nix b/checks/data-mesher/default.nix index 029b2277d..653cc266a 100644 --- a/checks/data-mesher/default.nix +++ b/checks/data-mesher/default.nix @@ -1,78 +1,86 @@ -(import ../lib/test-inventory.nix) ( - { lib, ... }: - let +{ + pkgs, + self, + clanLib, + ... +}: +clanLib.test.makeTestClan { + inherit pkgs self; + nixosTest = ( + { lib, ... }: + let + machines = [ + "admin" + "peer" + "signer" + ]; + in + { + name = "data-mesher"; - machines = [ - "admin" - "peer" - "signer" - ]; - in - { - name = "data-mesher"; - - clan = { - directory = ./.; - inventory = { - machines = lib.genAttrs machines (_: { }); - services = { - data-mesher.default = { - roles.peer.machines = [ "peer" ]; - roles.admin.machines = [ "admin" ]; - roles.signer.machines = [ "signer" ]; + clan = { + directory = ./.; + inventory = { + machines = lib.genAttrs machines (_: { }); + services = { + data-mesher.default = { + roles.peer.machines = [ "peer" ]; + roles.admin.machines = [ "admin" ]; + roles.signer.machines = [ "signer" ]; + }; }; }; }; - }; - defaults = - { config, ... }: - { - environment.systemPackages = [ - config.services.data-mesher.package - ]; + defaults = + { config, ... }: + { + environment.systemPackages = [ + config.services.data-mesher.package + ]; - clan.data-mesher.network.interface = "eth1"; - clan.data-mesher.bootstrapNodes = [ - "[2001:db8:1::1]:7946" # peer1 - "[2001:db8:1::2]:7946" # peer2 - ]; + clan.data-mesher.network.interface = "eth1"; + clan.data-mesher.bootstrapNodes = [ + "[2001:db8:1::1]:7946" # peer1 + "[2001:db8:1::2]:7946" # peer2 + ]; - # speed up for testing - services.data-mesher.settings = { - cluster.join_interval = lib.mkForce "2s"; - cluster.push_pull_interval = lib.mkForce "5s"; + # speed up for testing + services.data-mesher.settings = { + cluster.join_interval = lib.mkForce "2s"; + cluster.push_pull_interval = lib.mkForce "5s"; + }; }; + + nodes = { + admin.clan.data-mesher.network.tld = "foo"; }; - nodes = { - admin.clan.data-mesher.network.tld = "foo"; - }; + # TODO Add better test script. + testScript = '' - # TODO Add better test script. - testScript = '' + def resolve(node, success = {}, fail = [], timeout = 60): + for hostname, ips in success.items(): + for ip in ips: + node.wait_until_succeeds(f"getent ahosts {hostname} | grep {ip}", timeout) - def resolve(node, success = {}, fail = [], timeout = 60): - for hostname, ips in success.items(): - for ip in ips: - node.wait_until_succeeds(f"getent ahosts {hostname} | grep {ip}", timeout) + for hostname in fail: + node.wait_until_fails(f"getent ahosts {hostname}") - for hostname in fail: - node.wait_until_fails(f"getent ahosts {hostname}") + start_all() - start_all() + admin.wait_for_unit("data-mesher") + signer.wait_for_unit("data-mesher") + peer.wait_for_unit("data-mesher") - admin.wait_for_unit("data-mesher") - signer.wait_for_unit("data-mesher") - peer.wait_for_unit("data-mesher") - - # check dns resolution - for node in [admin, signer, peer]: - resolve(node, { - "admin.foo": ["2001:db8:1::1", "192.168.1.1"], - "peer.foo": ["2001:db8:1::2", "192.168.1.2"], - "signer.foo": ["2001:db8:1::3", "192.168.1.3"] - }) - ''; - } -) + # check dns resolution + for node in [admin, signer, peer]: + resolve(node, { + "admin.foo": ["2001:db8:1::1", "192.168.1.1"], + "peer.foo": ["2001:db8:1::2", "192.168.1.2"], + "signer.foo": ["2001:db8:1::3", "192.168.1.3"] + }) + ''; + } + ); +} diff --git a/checks/dummy-inventory-test/default.nix b/checks/dummy-inventory-test/default.nix index bafac94e9..46e42ecec 100644 --- a/checks/dummy-inventory-test/default.nix +++ b/checks/dummy-inventory-test/default.nix @@ -1,67 +1,76 @@ -(import ../lib/test-inventory.nix) ( - { ... }: - { - # This tests the compatibility of the inventory - # With the test framework - # - legacy-modules - # - clan.service modules - name = "dummy-inventory-test"; +{ + pkgs, + self, + clanLib, + ... +}: +clanLib.test.makeTestClan { + inherit pkgs self; + nixosTest = ( + { ... }: + { + # This tests the compatibility of the inventory + # With the test framework + # - legacy-modules + # - clan.service modules + name = "dummy-inventory-test"; - clan = { - directory = ./.; - inventory = { - machines.peer1 = { }; - machines.admin1 = { }; - services = { - legacy-module.default = { - roles.peer.machines = [ "peer1" ]; - roles.admin.machines = [ "admin1" ]; + clan = { + directory = ./.; + inventory = { + machines.peer1 = { }; + machines.admin1 = { }; + services = { + legacy-module.default = { + roles.peer.machines = [ "peer1" ]; + roles.admin.machines = [ "admin1" ]; + }; + }; + instances."test" = { + module.name = "new-service"; + roles.peer.machines.peer1 = { }; }; - }; - instances."test" = { - module.name = "new-service"; - roles.peer.machines.peer1 = { }; - }; - modules = { - legacy-module = ./legacy-module; - new-service = { - _class = "clan.service"; - manifest.name = "new-service"; - roles.peer = { }; - perMachine = { - nixosModule = { - # This should be generated by: - # ./pkgs/scripts/update-vars.py inventory-test-framework-compatibility-test - clan.core.vars.generators.new-service = { - files.hello = { - secret = false; - deploy = true; + modules = { + legacy-module = ./legacy-module; + new-service = { + _class = "clan.service"; + manifest.name = "new-service"; + roles.peer = { }; + perMachine = { + nixosModule = { + # This should be generated by: + # ./pkgs/scripts/update-vars.py inventory-test-framework-compatibility-test + clan.core.vars.generators.new-service = { + files.hello = { + secret = false; + deploy = true; + }; + script = '' + # This is a dummy script that does nothing + echo "This is a dummy script" > $out/hello + ''; }; - script = '' - # This is a dummy script that does nothing - echo "This is a dummy script" > $out/hello - ''; }; }; }; }; }; }; - }; - testScript = - { nodes, ... }: - '' - start_all() - admin1.wait_for_unit("multi-user.target") - peer1.wait_for_unit("multi-user.target") - # Provided by the legacy module - print(admin1.succeed("systemctl status dummy-service")) - print(peer1.succeed("systemctl status dummy-service")) + testScript = + { nodes, ... }: + '' + start_all() + admin1.wait_for_unit("multi-user.target") + peer1.wait_for_unit("multi-user.target") + # Provided by the legacy module + print(admin1.succeed("systemctl status dummy-service")) + print(peer1.succeed("systemctl status dummy-service")) - # peer1 should have the 'hello' file - peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.hello.path}") - ''; - } -) + # peer1 should have the 'hello' file + peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.hello.path}") + ''; + } + ); +} diff --git a/checks/flake-module.nix b/checks/flake-module.nix index 0ced0c78c..2e7526864 100644 --- a/checks/flake-module.nix +++ b/checks/flake-module.nix @@ -27,9 +27,10 @@ in let nixosTestArgs = { # reference to nixpkgs for the current system - inherit pkgs; + inherit pkgs lib; # this gives us a reference to our flake but also all flake inputs inherit self; + inherit (self) clanLib; }; nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) { # import our test diff --git a/checks/installation/flake-module.nix b/checks/installation/flake-module.nix index 35632bb02..30c909769 100644 --- a/checks/installation/flake-module.nix +++ b/checks/installation/flake-module.nix @@ -99,7 +99,7 @@ in imports = [ (modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests (modulesPath + "/profiles/qemu-guest.nix") - ../lib/minify.nix + self.clanLib.test.minifyModule ]; networking.hostName = "test-install-machine"; diff --git a/checks/lib/container-test.nix b/checks/lib/container-test.nix index 31ba285a5..b0d1935ba 100644 --- a/checks/lib/container-test.nix +++ b/checks/lib/container-test.nix @@ -13,7 +13,7 @@ in { config, options, ... }: { imports = [ - ./minify.nix + self.clanLib.test.minifyModule ]; config = lib.mkMerge [ (lib.optionalAttrs (options ? clan) { diff --git a/checks/lib/test-base.nix b/checks/lib/test-base.nix index 7c37ee51a..72fcc8826 100644 --- a/checks/lib/test-base.nix +++ b/checks/lib/test-base.nix @@ -11,7 +11,7 @@ in { config, options, ... }: { imports = [ - ./minify.nix + self.clanLib.test.minifyModule ]; config = lib.mkMerge [ (lib.optionalAttrs (options ? clan) { diff --git a/checks/lib/test-inventory.nix b/checks/lib/test-inventory.nix deleted file mode 100644 index 60939c13f..000000000 --- a/checks/lib/test-inventory.nix +++ /dev/null @@ -1,89 +0,0 @@ -test: -{ pkgs, self, ... }: -let - inherit (pkgs) lib; - inherit (lib) - mkOption - flip - mapAttrs - types - ; - nixos-lib = import (pkgs.path + "/nixos/lib") { }; -in -(nixos-lib.runTest ( - { config, ... }: - let - clanFlakeResult = config.clan; - in - { - imports = [ test ]; - options = { - clanSettings = mkOption { - default = { }; - type = types.submodule { - options = { - clan-core = mkOption { default = self; }; - nixpkgs = mkOption { default = self.inputs.nixpkgs; }; - nix-darwin = mkOption { default = self.inputs.nix-darwin; }; - }; - }; - }; - - clan = mkOption { - default = { }; - type = types.submoduleWith { - specialArgs = { - inherit (config.clanSettings) - clan-core - nixpkgs - nix-darwin - ; - }; - modules = [ - self.clanLib.buildClanModule.flakePartsModule - ]; - }; - }; - }; - config = { - nodes = flip mapAttrs clanFlakeResult.clanInternals.inventoryClass.machines ( - machineName: attrs: { - imports = attrs.machineImports ++ [ self.nixosModules.clanCore ]; - clan.core.settings.directory = "${config.clan.directory}"; - clan.core.settings.machine.name = machineName; - } - ); - hostPkgs = pkgs; - # speed-up evaluation - defaults = ( - { config, ... }: - { - imports = [ - ./minify.nix - ]; - documentation.enable = lib.mkDefault false; - nix.settings.min-free = 0; - system.stateVersion = config.system.nixos.release; - boot.initrd.systemd.enable = false; - - # setup for sops - sops.age.keyFile = "/run/age-key.txt"; - system.activationScripts = - { - setupSecrets.deps = [ "age-key" ]; - age-key.text = '' - echo AGE-SECRET-KEY-1PL0M9CWRCG3PZ9DXRTTLMCVD57U6JDFE8K7DNVQ35F4JENZ6G3MQ0RQLRV > /run/age-key.txt - ''; - } - // lib.optionalAttrs (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets != { }) { - setupSecretsForUsers.deps = [ "age-key" ]; - }; - } - ); - - _module.args = { inherit self; }; - # to accept external dependencies such as disko - node.specialArgs.self = self; - }; - } -)).config.result diff --git a/lib/default.nix b/lib/default.nix index f6b038528..9e852e89e 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -25,6 +25,7 @@ lib.fix (clanLib: { buildClanModule = clanLib.callLib ./build-clan { }; inventory = clanLib.callLib ./inventory { }; modules = clanLib.callLib ./inventory/frontmatter { }; + test = clanLib.callLib ./tests { }; # Plain imports. values = import ./introspection { inherit lib; }; diff --git a/lib/tests/default.nix b/lib/tests/default.nix new file mode 100644 index 000000000..d048c9ed1 --- /dev/null +++ b/lib/tests/default.nix @@ -0,0 +1,92 @@ +{ lib, clanLib }: +let + inherit (lib) + mkOption + types + ; +in +{ + minifyModule = ./minify.nix; + # A function that returns an extension to runTest + makeTestClan = + { + nixosTest, + pkgs, + self, + ... + }: + let + nixos-lib = import (pkgs.path + "/nixos/lib") { }; + in + (nixos-lib.runTest ( + { config, ... }: + let + clanFlakeResult = config.clan; + in + { + imports = [ nixosTest ]; + options = { + clanSettings = mkOption { + default = { }; + type = types.submodule { + options = { + clan-core = mkOption { default = self; }; + nixpkgs = mkOption { default = self.inputs.nixpkgs; }; + nix-darwin = mkOption { default = self.inputs.nix-darwin; }; + }; + }; + }; + + clan = mkOption { + default = { }; + type = types.submoduleWith { + specialArgs = { + inherit (config.clanSettings) + clan-core + nixpkgs + nix-darwin + ; + }; + modules = [ + clanLib.buildClanModule.flakePartsModule + ]; + }; + }; + }; + config = { + nodes = clanFlakeResult.clanInternals.nixosModules; + hostPkgs = pkgs; + # speed-up evaluation + defaults = ( + { config, ... }: + { + imports = [ + clanLib.test.minifyModule + ]; + documentation.enable = lib.mkDefault false; + nix.settings.min-free = 0; + system.stateVersion = config.system.nixos.release; + boot.initrd.systemd.enable = false; + + # setup for sops + sops.age.keyFile = "/run/age-key.txt"; + system.activationScripts = + { + setupSecrets.deps = [ "age-key" ]; + age-key.text = '' + echo AGE-SECRET-KEY-1PL0M9CWRCG3PZ9DXRTTLMCVD57U6JDFE8K7DNVQ35F4JENZ6G3MQ0RQLRV > /run/age-key.txt + ''; + } + // lib.optionalAttrs (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets != { }) { + setupSecretsForUsers.deps = [ "age-key" ]; + }; + } + ); + + # to accept external dependencies such as disko + _module.args = { inherit self; }; + node.specialArgs.self = self; + }; + } + )).config.result; +} diff --git a/checks/lib/minify.nix b/lib/tests/minify.nix similarity index 62% rename from checks/lib/minify.nix rename to lib/tests/minify.nix index 9b1c3e70e..411bf687d 100644 --- a/checks/lib/minify.nix +++ b/lib/tests/minify.nix @@ -1,3 +1,6 @@ +# This is a module to reduce the size of the NixOS configuration +# Used by the tests +# It unsets some unnecessary options { lib, ... }: { nixpkgs.flake.setFlakeRegistry = false;