diff --git a/checks/dummy-inventory-test-from-flake/default.nix b/checks/dummy-inventory-test-from-flake/default.nix new file mode 100644 index 000000000..8e2d4555a --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/default.nix @@ -0,0 +1,61 @@ +{ + pkgs, + nixosLib, + clan-core, + ... +}: + +nixosLib.runTest ( + { hostPkgs, config, ... }: + { + imports = [ + clan-core.modules.nixosVmTest.clanTest + ]; + + hostPkgs = pkgs; + + # This tests the compatibility of the inventory + # With the test framework + # - legacy-modules + # - clan.service modules + name = "dummy-inventory-test-from-flake"; + + clan.test.fromFlake = ./.; + + testScript = + { nodes, ... }: + '' + ${clan-core.legacyPackages.${hostPkgs.system}.setupNixInNixPython} + + def run_clan(cmd: list[str], **kwargs) -> str: + import subprocess + clan = "${clan-core.packages.${hostPkgs.system}.clan-cli}/bin/clan" + clan_args = ["--flake", "${config.clan.test.flakeForSandbox}"] + return subprocess.run( + ["${hostPkgs.util-linux}/bin/unshare", "--user", "--map-user", "1000", "--map-group", "1000", clan, *cmd, *clan_args], + **kwargs, + check=True, + ).stdout + + 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.not-a-secret.path}") + + ls_out = peer1.succeed("ls -la ${nodes.peer1.clan.core.vars.generators.new-service.files.a-secret.path}") + # Check that the file is owned by 'nobody' + assert "nobody" in ls_out, f"File is not owned by 'nobody': {ls_out}" + # Check that the file is in the 'users' group + assert "users" in ls_out, f"File is not in the 'users' group: {ls_out}" + # Check that the file is in the '0644' mode + assert "-rw-r--r--" in ls_out, f"File is not in the '0644' mode: {ls_out}" + + run_clan(["machines", "list"]) + ''; + } +) diff --git a/checks/dummy-inventory-test-from-flake/flake.nix b/checks/dummy-inventory-test-from-flake/flake.nix new file mode 100644 index 000000000..575f65584 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/flake.nix @@ -0,0 +1,70 @@ +{ + inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz"; + inputs.nixpkgs.follows = "clan-core/nixpkgs"; + + outputs = + { self, clan-core, ... }: + let + # Usage see: https://docs.clan.lol + clan = clan-core.clanLib.buildClan { + inherit self; + + inventory = + { ... }: + { + meta.name = "foo"; + 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 = { }; + }; + + modules = { + legacy-module = ./legacy-module; + }; + }; + + modules.new-service = { + _class = "clan.service"; + manifest.name = "new-service"; + roles.peer = { }; + perMachine = { + nixosModule = { + # This should be generated by: + # nix run .#generate-test-vars -- checks/dummy-inventory-test dummy-inventory-test + clan.core.vars.generators.new-service = { + files.not-a-secret = { + secret = false; + deploy = true; + }; + files.a-secret = { + secret = true; + deploy = true; + owner = "nobody"; + group = "users"; + mode = "0644"; + }; + script = '' + # This is a dummy script that does nothing + echo -n "not-a-secret" > $out/not-a-secret + echo -n "a-secret" > $out/a-secret + ''; + }; + }; + }; + }; + }; + in + { + # all machines managed by Clan + inherit (clan) nixosConfigurations nixosModules clanInternals; + }; +} diff --git a/checks/dummy-inventory-test-from-flake/legacy-module/README.md b/checks/dummy-inventory-test-from-flake/legacy-module/README.md new file mode 100644 index 000000000..2a72080ce --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/legacy-module/README.md @@ -0,0 +1,10 @@ +--- +description = "Set up dummy-module" +categories = ["System"] +features = [ "inventory" ] + +[constraints] +roles.admin.min = 1 +roles.admin.max = 1 +--- + diff --git a/checks/dummy-inventory-test-from-flake/legacy-module/roles/admin.nix b/checks/dummy-inventory-test-from-flake/legacy-module/roles/admin.nix new file mode 100644 index 000000000..a56780406 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/legacy-module/roles/admin.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ../shared.nix + ]; +} diff --git a/checks/dummy-inventory-test-from-flake/legacy-module/roles/peer.nix b/checks/dummy-inventory-test-from-flake/legacy-module/roles/peer.nix new file mode 100644 index 000000000..a56780406 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/legacy-module/roles/peer.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ../shared.nix + ]; +} diff --git a/checks/dummy-inventory-test-from-flake/legacy-module/shared.nix b/checks/dummy-inventory-test-from-flake/legacy-module/shared.nix new file mode 100644 index 000000000..92b7418ca --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/legacy-module/shared.nix @@ -0,0 +1,34 @@ +{ config, ... }: +{ + systemd.services.dummy-service = { + enable = true; + description = "Dummy service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + generated_password_path="${config.clan.core.vars.generators.dummy-generator.files.generated-password.path}" + if [ ! -f "$generated_password_path" ]; then + echo "Generated password file not found: $generated_password_path" + exit 1 + fi + host_id_path="${config.clan.core.vars.generators.dummy-generator.files.host-id.path}" + if [ ! -e "$host_id_path" ]; then + echo "Host ID file not found: $host_id_path" + exit 1 + fi + ''; + }; + + # TODO: add and prompt and make it work in the test framework + clan.core.vars.generators.dummy-generator = { + files.host-id.secret = false; + files.generated-password.secret = true; + script = '' + echo $RANDOM > "$out"/host-id + echo $RANDOM > "$out"/generated-password + ''; + }; +} diff --git a/checks/dummy-inventory-test-from-flake/sops/machines/admin1/key.json b/checks/dummy-inventory-test-from-flake/sops/machines/admin1/key.json new file mode 100755 index 000000000..0e9408bca --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/machines/admin1/key.json @@ -0,0 +1,6 @@ +[ + { + "publickey": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m", + "type": "age" + } +] diff --git a/checks/dummy-inventory-test-from-flake/sops/machines/peer1/key.json b/checks/dummy-inventory-test-from-flake/sops/machines/peer1/key.json new file mode 100755 index 000000000..5d39fd60a --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/machines/peer1/key.json @@ -0,0 +1,6 @@ +[ + { + "publickey": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2", + "type": "age" + } +] diff --git a/checks/dummy-inventory-test-from-flake/sops/secrets/admin1-age.key/secret b/checks/dummy-inventory-test-from-flake/sops/secrets/admin1-age.key/secret new file mode 100644 index 000000000..6d713ad91 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/secrets/admin1-age.key/secret @@ -0,0 +1,15 @@ +{ + "data": "ENC[AES256_GCM,data:GPpsUhSzWPtTP8EUNKsobFXjYqDldhkkIH6hBk11RsDLAGWdhVrwcISGbhsWpYhvAdPKA84DB6Zqyh9lL2bLM9//ybC1kzY20BQ=,iv:NrxMLdedT2FCkUAD00SwsAHchIsxWvqe7BQekWuJcxw=,tag:pMDXcMyHnLF2t3Qhb1KolA==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzb2tWb1ExKzdmUTRzaGVj\nK3cyYTBHZTJwVjM1SzUvbHFiMnVhY05iKzFZCnJTSE1VSVdpcUFLSEJuaE1CZzJD\nWjZxYzN2cUltdThNMVRKU3FIb20vUXMKLS0tIFlHQXRIdnMybDZFUVEzWlQrc1dw\nbUxhZURXblhHd0pka0JIK1FTZEVqdUEKI/rfxQRBc+xGRelhswkJQ9GcZs6lzfgy\nuCxS5JI9npdPLQ/131F3b21+sP5YWqks41uZG+vslM1zQ+BlENNhDw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-05-04T12:44:13Z", + "mac": "ENC[AES256_GCM,data:fWxLHXBWolHVxv6Q7utcy6OVLV13ziswrIYyNKiwy1vsU8i7xvvuGO1HlnE+q43D2WuHR53liKq1UHuf1JMrWzTwZ0PYe+CVugtoEtbR2qu3rK/jAkOyMyhmmHzmf6Rp4ZMCzKgZeC/X2bDKY/z0firHAvjWydEyogutHpvtznM=,iv:OQI3FfkLneqbdztAXVQB3UkHwDPK+0hWu5hZ9m8Oczg=,tag:em6GfS2QHsXs391QKPxfmA==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/dummy-inventory-test-from-flake/sops/secrets/admin1-age.key/users/admin b/checks/dummy-inventory-test-from-flake/sops/secrets/admin1-age.key/users/admin new file mode 120000 index 000000000..9e21a9938 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/secrets/admin1-age.key/users/admin @@ -0,0 +1 @@ +../../../users/admin \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/sops/secrets/peer1-age.key/secret b/checks/dummy-inventory-test-from-flake/sops/secrets/peer1-age.key/secret new file mode 100644 index 000000000..16bd52683 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/secrets/peer1-age.key/secret @@ -0,0 +1,15 @@ +{ + "data": "ENC[AES256_GCM,data:W3cOkUYL5/YulW2pEISyTlMaA/t7/WBE7BoCdFlqrqgaCL7tG4IV2HgjiPWzIVMs0zvDSaghdEvAIoB4wOf470d1nSWs0/E8SDk=,iv:wXXaZIw3sPY8L/wxsu7+C5v+d3RQRuwxZRP4YLkS8K4=,tag:HeK4okj7O7XDA9JDz2KULw==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxRC83b3dtSVpXcGovNnVs\nTzFka2J2MEFhYkF1ajVrdjMrNUtPWGRObjM4Cm5zSUR5OGw0T0FaL3BaWmR6L29W\nU2syMFIyMUhFRUZpWFpCT28vWko2ZU0KLS0tIFpHK3BjU1V1L0FrMGtwTGFuU3Mz\nRkV5VjI2Vndod202bUR3RWQwNXpmVzQKNk8/y7M62wTIIKqY4r3ZRk5aUCRUfine\n1LUSHMKa2bRe+hR7nS7AF4BGXp03h2UPY0FP5+U5q8XuIj1jfMX8kg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-05-04T12:44:16Z", + "mac": "ENC[AES256_GCM,data:yTkQeFvKrN1+5FP+yInsaRWSAG+ZGG0uWF3+gVRvzJTFxab8kT2XkAMc+4D7SKgcjsmwBBb77GNoAKaKByhZ92UaCfZ2X66i7ZmYUwLM1NVVmm+xiwwjsh7PJXlZO/70anTzd1evtlZse0jEmRnV5Y0F0M6YqXmuwU+qGUJU2F8=,iv:sy6ozhXonWVruaQfa7pdEoV5GkNZR/UbbINKAPbgWeg=,tag:VMruQ1KExmlMR7TsGNgMlg==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/dummy-inventory-test-from-flake/sops/secrets/peer1-age.key/users/admin b/checks/dummy-inventory-test-from-flake/sops/secrets/peer1-age.key/users/admin new file mode 120000 index 000000000..9e21a9938 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/secrets/peer1-age.key/users/admin @@ -0,0 +1 @@ +../../../users/admin \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/sops/users/admin/key.json b/checks/dummy-inventory-test-from-flake/sops/users/admin/key.json new file mode 100644 index 000000000..e408aa96b --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/sops/users/admin/key.json @@ -0,0 +1,4 @@ +{ + "publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "type": "age" +} diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/machines/admin1 b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/machines/admin1 new file mode 120000 index 000000000..ca09a1bbd --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/machines/admin1 @@ -0,0 +1 @@ +../../../../../../sops/machines/admin1 \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/secret b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/secret new file mode 100644 index 000000000..0073dcc9c --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:T8edCvw=,iv:7/G5xt5fv38I9uFzk7WMIr9xQdz/6lFxqOC+18HBg8Q=,tag:F39Cxbgmzml+lZLsZ59Kmg==,type:str]", + "sops": { + "age": [ + { + "recipient": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPNUhiYkZWK3dPMHNiRTVM\nRHNvaHFsOFp1c0UxQitwVG0zY01MNDZRV1E4CjEybENoTVIzN29vQ3FtUTRSYmFU\nNXIzQllVSllXRGN2M1B6WXJLdHZSajgKLS0tIDllZ0ZmZUcxMHhDQUpUOEdWbmkv\neUQweHArYTdFSmNteVpuQ3BKdnh0Y0UKs8Hm3D+rXRRfpUVSZM3zYjs6b9z8g10D\nGTkvreUMim4CS22pjdQ3eNA9TGeDXfWXE7XzwXLCb+wVcf7KwbDmvg==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKSDhpT3cvck9PenZYVEZH\ndFQreVRBdG93L1dBUGlvYjFWcDlHWUJsZUVBCm9DMTJ4UytiYzlEVHNWdUcwS1ds\nT0dhbzAzNDdmbDBCU0dvL2xNeHpXcGsKLS0tIFArbmpsbzU3WnpJdUt1MGN0L1d0\nV1JkTDJYWUxsbmhTQVNOeVRaSUhTODQKk9Vph2eldS5nwuvVX0SCsxEm4B+sO76Z\ndIjJ3OQxzoZmXMaOOuKHC5U0Y75Qn7eXC43w5KHsl2CMIUYsBGJOZw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-05-04T12:44:14Z", + "mac": "ENC[AES256_GCM,data:6fKrS1eLLUWlHkQpxLFXBRk6f2wa5ADLMViVvYXXGU24ayl9UuNSKrCRHp9cbzhqhti3HdwyNt6TM+2X6qhiiAQanKEB2PF7JRYX74NfNKil9BEDjt5AqqtpSgVv5l7Ku/uSHaPkd2sDmzHsy5Q4bSGxJQokStk1kidrwle+mbc=,iv:I/Aad82L/TCxStM8d8IZICUrwdjRbGx2fuGWqexr21o=,tag:BfgRbGUxhPZzK2fLik1kxA==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/users/admin b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/generated-password/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/host-id/value b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/host-id/value new file mode 100644 index 000000000..b222a5fe6 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/admin1/dummy-generator/host-id/value @@ -0,0 +1 @@ +18650 diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/machines/peer1 b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/machines/peer1 new file mode 120000 index 000000000..3e5f3fae3 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/machines/peer1 @@ -0,0 +1 @@ +../../../../../../sops/machines/peer1 \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/secret b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/secret new file mode 100644 index 000000000..bc4778493 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:vp0yW0Gt,iv:FO2cy+UpEl5aRay/LUGu//c82QiVxuKuGSaVh0rGJvc=,tag:vf2RAOPpcRW0HwxHoGy17A==,type:str]", + "sops": { + "age": [ + { + "recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjaFVNMEd2YUxpSm5XVVRi\nY2ZUc3NTOStJUFNMWWVPQTgxZ2tCK1QrMW1ZCjYwMlA4dkIzSlc0TGtvZjcyK3Bi\nM3pob2JOOFUyeVJ6M2JpaTRCZlc1R0kKLS0tIDJMb1dFcVRWckhwYWNCQng0RlFO\nTkw3OGt4dkFIZVY5aVEzZE5mMzJSM0EKUv8bUqg48L2FfYVUVlpXvyZvPye699of\nG6PcjLh1ZMbNCfnsCzr+P8Vdk/F4J/ifxL66lRGfu2xOLxwciwQ+5Q==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnZ2dDbVhoQngxM3lTSmZF\nUTAwS1lCTGhEMU1GVXpFUzlIUFdqZy9LajF3Ck9mdVpBRjlyVUNhZXZIUFZjUzF1\nNlhFN28vNmwzcUVkNmlzUnpkWjJuZE0KLS0tIHpXVHVlNk9vU1ZPTGRrYStWbmRO\nbDM4U2o1SlEwYWtqOXBqd3BFUTAvMHcKkI8UVd0v+x+ELZ5CoGq9DzlA6DnVNU2r\nrV9wLfbFd7RHxS0/TYZh5tmU42nO3iMYA9FqERQXCtZgXS9KvfqHwQ==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-05-04T12:44:18Z", + "mac": "ENC[AES256_GCM,data:1ZZ+ZI1JsHmxTov1bRijfol3kTkyheg2o3ivLsMHRhCmScsUry97hQJchF78+y2Izt7avaQEHYn6pVbYt/0rLrSYD7Ru7ITVxXoYHOiN5Qb98masUzpibZjrdyg5nO+LW5/Hmmwsc3yn/+o3IH1AUYpsxlJRdnHHCmoSOFaiFFM=,iv:OQlgmpOTw4ljujNzqwQ5/0Mz8pQpCSUtqRvj3FJAxDs=,tag:foZvdeW7gK9ZVKkWqnlxGA==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/users/admin b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/generated-password/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/host-id/value b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/host-id/value new file mode 100644 index 000000000..e53a654ed --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/dummy-generator/host-id/value @@ -0,0 +1 @@ +6745 diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/machines/peer1 b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/machines/peer1 new file mode 120000 index 000000000..3e5f3fae3 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/machines/peer1 @@ -0,0 +1 @@ +../../../../../../sops/machines/peer1 \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/secret b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/secret new file mode 100644 index 000000000..60561eee3 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:prFl0EJy8bM=,iv:zITWxf+6Ebk0iB5vhhd7SBQa1HFrIJXm8xpSM+D9I0M=,tag:NZCRMCs1SzNKLBu/KUDKMQ==,type:str]", + "sops": { + "age": [ + { + "recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0S0RZRWxaZVZvTUhjdWVL\naU9WZmtEcm1qa2JsRmdvdmZmNENMaWFEVUFRCmdoVnRXSGlpRlFjNmVVbDJ5VnFT\nMnVJUlVnM3lxNmZCRTdoRVJ4NW1oYWcKLS0tIFFNbXBFUk1RWnlUTW1SeG1vYzlM\nVVpEclFVOE9PWWQxVkZ0eEgwWndoRWcKDAOHe+FIxqGsc6LhxMy164qjwG6t2Ei2\nP0FSs+bcKMDpudxeuxCjnDm/VoLxOWeuqkB+9K2vSm2W/c/fHTSbrA==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2VU5jOEpwYUtDVEVFcVpU\nQkExTVZ3ejZHcGo5TG8zdUQwNktoV09WdUZvCmQ0dE1TOWRFbTlxdVd4WWRxd3VF\nQUNTTkNNT3NKYjQ5dEJDY0xVZ3pZVUUKLS0tIDFjajRZNFJZUTdNeS8yN05FMFZU\ncEtjRjhRbGE0MnRLdk10NkFLMkxqencKGzJ66dHluIghH04RV/FccfEQP07yqnfb\n25Hi0XIVJfXBwje4UEyszrWTxPPwVXdQDQmoNKf76Qy2jYqJ56uksw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-05-04T12:44:20Z", + "mac": "ENC[AES256_GCM,data:FIkilsni5kOdNlVwDuLsQ/zExypHRWdqIBQDNWMLTwe8OrsNPkX+KYutUvt9GaSoGv4iDULaMRoizO/OZUNfc2d8XYSdj0cxOG1Joov4GPUcC/UGyNuQneAejZBKolvlnidKZArofnuK9g+lOTANEUtEXUTnx8L+VahqPZayQas=,iv:NAo6sT3L8OOB3wv1pjr3RY2FwXgVmZ4N0F4BEX4YPUY=,tag:zHwmXygyvkdpASZCodQT9Q==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.2" + } +} diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/users/admin b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/a-secret/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/not-a-secret/value b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/not-a-secret/value new file mode 100644 index 000000000..c9cf44c35 --- /dev/null +++ b/checks/dummy-inventory-test-from-flake/vars/per-machine/peer1/new-service/not-a-secret/value @@ -0,0 +1 @@ +not-a-secret \ No newline at end of file diff --git a/checks/flake-module.nix b/checks/flake-module.nix index bfb5f8caf..ed3122540 100644 --- a/checks/flake-module.nix +++ b/checks/flake-module.nix @@ -51,6 +51,7 @@ in postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs; dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs; + dummy-inventory-test-from-flake = import ./dummy-inventory-test-from-flake nixosTestArgs; data-mesher = import ./data-mesher nixosTestArgs; } // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "aarch64-linux") { diff --git a/lib/clanTest/flake-module.nix b/lib/clanTest/flake-module.nix index cbbace26a..18ca7560e 100644 --- a/lib/clanTest/flake-module.nix +++ b/lib/clanTest/flake-module.nix @@ -7,11 +7,10 @@ let inherit (lib) flatten - flip - mapAttrs' mapAttrsToList + mkForce + mkIf mkOption - removePrefix types unique ; @@ -24,9 +23,48 @@ in flake.modules.nixosVmTest.clanTest = { config, hostPkgs, ... }: let - clanFlakeResult = config.clan; testName = config.name; + clan-core = self; + + inherit (lib) + filterAttrs + flip + hasPrefix + intersectAttrs + mapAttrs' + pathExists + removePrefix + throwIf + ; + + # only relevant if config.clan.test.fromFlake is used + importFlake = + flakeDir: + let + flakeExpr = import (flakeDir + "/flake.nix"); + inputs = intersectAttrs flakeExpr.inputs clan-core.inputs; + flake = flakeExpr.outputs ( + inputs + // { + self = flake // { + outPath = flakeDir; + }; + clan-core = clan-core; + } + ); + in + throwIf (pathExists ( + flakeDir + "/flake.lock" + )) "Test ${testName} should not have a flake.lock file" flake; + + clanFlakeResult = + if config.clan.test.fromFlake != null then importFlake config.clan.test.fromFlake else config.clan; + + machineModules = flip filterAttrs clanFlakeResult.nixosModules ( + name: _module: hasPrefix "clan-machine-" name + ); + update-vars-script = "${ self.packages.${hostPkgs.system}.generate-test-vars }/bin/generate-test-vars"; @@ -48,7 +86,7 @@ in ); vars-check = - hostPkgs.runCommand "update-vars-check" + hostPkgs.runCommand "update-vars-check-${testName}" { nativeBuildInputs = generatorRuntimeInputs ++ [ hostPkgs.nix @@ -89,6 +127,16 @@ in fi touch $out ''; + + # the test's flake.nix with locked clan-core input + flakeForSandbox = hostPkgs.runCommand "offline-flake-for-test-${config.name}" { } '' + cp -r ${config.clan.directory} $out + chmod +w -R $out + substituteInPlace $out/flake.nix \ + --replace-fail \ + "https://git.clan.lol/clan/clan-core/archive/main.tar.gz" \ + "${clan-core.packages.${hostPkgs.system}.clan-core-flake}" + ''; in { imports = [ @@ -115,6 +163,9 @@ in nixpkgs nix-darwin ; + # By default clan.directory defaults to self, but we don't + # have a sensible default for self here + self = throw "set clan.directory in the test"; }; modules = [ clanLib.buildClanModule.flakePartsModule @@ -134,6 +185,28 @@ in type = types.bool; description = "Whether to use containers for the test."; }; + test.fromFlake = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + path to a directory containing a `flake.nix` defining the clan + + Only use this if the clan CLI needs to be used inside the test. + Otherwise, use the other clan.XXX options instead to specify the clan. + + Loads the clan from a flake instead of using clan.XXX options. + This has the benefit that a real flake.nix will be available in the test. + This is useful to test CLI interactions which require a flake.nix. + + Using this introduces dependencies that should otherwise be avoided. + ''; + }; + test.flakeForSandbox = mkOption { + default = flakeForSandbox; + type = types.path; + description = "The flake.nix to use for the test."; + readOnly = true; + }; }; } ]; @@ -141,10 +214,12 @@ in }; }; config = { + clan.directory = mkIf (config.clan.test.fromFlake != null) (mkForce config.clan.test.fromFlake); + # Inherit all nodes from the clan # i.e. nodes.jon <- clan.machines.jon # clanInternals.nixosModules contains nixosModules per node - nodes = flip mapAttrs' clanFlakeResult.nixosModules ( + nodes = flip mapAttrs' machineModules ( name: machineModule: { name = removePrefix "clan-machine-" name; value = machineModule; @@ -169,11 +244,6 @@ in clanLib.test.sopsModule ]; - # Disable documentation - # This is nice to speed up the evaluation - # And also suppresses any warnings or errors about the documentation - documentation.enable = lib.mkDefault false; - # Disable garbage collection during the test # https://nix.dev/manual/nix/2.28/command-ref/conf-file.html?highlight=min-free#available-settings nix.settings.min-free = 0; diff --git a/lib/test/minify.nix b/lib/test/minify.nix index 411bf687d..2f7451840 100644 --- a/lib/test/minify.nix +++ b/lib/test/minify.nix @@ -8,4 +8,5 @@ nix.registry = lib.mkForce { }; documentation.doc.enable = false; documentation.man.enable = false; + documentation.enable = lib.mkDefault false; } diff --git a/pkgs/testing/flake-module.nix b/pkgs/testing/flake-module.nix index 539199148..4c6b1de41 100644 --- a/pkgs/testing/flake-module.nix +++ b/pkgs/testing/flake-module.nix @@ -1,19 +1,37 @@ { perSystem = { - legacyPackages.setupNixInNix = '' - export HOME=$TMPDIR - export NIX_STATE_DIR=$TMPDIR/nix - export NIX_CONF_DIR=$TMPDIR/etc - export IN_NIX_SANDBOX=1 - export CLAN_TEST_STORE=$TMPDIR/store - # required to prevent concurrent 'nix flake lock' operations - export LOCK_NIX=$TMPDIR/nix_lock - mkdir -p "$CLAN_TEST_STORE/nix/store" - mkdir -p "$CLAN_TEST_STORE/nix/var/nix/gcroots" - if [[ -n "''${closureInfo-}" ]]; then - xargs cp --recursive --target "$CLAN_TEST_STORE/nix/store" < "$closureInfo/store-paths" - nix-store --load-db --store "$CLAN_TEST_STORE" < "$closureInfo/registration" - fi - ''; + legacyPackages = { + setupNixInNix = '' + export HOME=$TMPDIR + export NIX_STATE_DIR=$TMPDIR/nix + export NIX_CONF_DIR=$TMPDIR/etc + export IN_NIX_SANDBOX=1 + export CLAN_TEST_STORE=$TMPDIR/store + # required to prevent concurrent 'nix flake lock' operations + export LOCK_NIX=$TMPDIR/nix_lock + mkdir -p "$CLAN_TEST_STORE/nix/store" + mkdir -p "$CLAN_TEST_STORE/nix/var/nix/gcroots" + if [[ -n "''${closureInfo-}" ]]; then + xargs cp --recursive --target "$CLAN_TEST_STORE/nix/store" < "$closureInfo/store-paths" + nix-store --load-db --store "$CLAN_TEST_STORE" < "$closureInfo/registration" + fi + ''; + setupNixInNixPython = '' + from os import environ + import subprocess + from pathlib import Path + environ['HOME'] = environ['TMPDIR'] + environ['NIX_STATE_DIR'] = environ['TMPDIR'] + '/nix' + environ['NIX_CONF_DIR'] = environ['TMPDIR'] + '/etc' + environ['IN_NIX_SANDBOX'] = '1' + environ['CLAN_TEST_STORE'] = environ['TMPDIR'] + '/store' + environ['LOCK_NIX'] = environ['TMPDIR'] + '/nix_lock' + Path(environ['CLAN_TEST_STORE'] + '/nix/store').mkdir(parents=True, exist_ok=True) + Path(environ['CLAN_TEST_STORE'] + '/nix/var/nix/gcroots').mkdir(parents=True, exist_ok=True) + if 'closureInfo' in environ: + subprocess.run(['cp', '--recursive', '--target', environ['CLAN_TEST_STORE'] + '/nix/store'] + environ['closureInfo'].split(), check=True) + subprocess.run(['nix-store', '--load-db', '--store', environ['CLAN_TEST_STORE']] + ['<', environ['closureInfo'] + '/registration'], shell=True, check=True) + ''; + }; }; }