Compare commits
300 Commits
hgl-ui-tex
...
lopter-lo-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2e4b98a93 | ||
|
|
25ce97dd5e | ||
|
|
bd361b2744 | ||
|
|
ac901f5656 | ||
|
|
8339ad605c | ||
|
|
3473aaa440 | ||
|
|
e983b10331 | ||
|
|
fcf5ccd115 | ||
|
|
6e7a67c830 | ||
|
|
1cb7c7d25f | ||
|
|
43b1744528 | ||
|
|
27d1dd4400 | ||
|
|
5d27af51d8 | ||
|
|
c737271585 | ||
|
|
f504a63e93 | ||
|
|
ca53d14656 | ||
|
|
09d8a2b3a9 | ||
|
|
2a67b09f51 | ||
|
|
3295e1561d | ||
|
|
9d0cec1422 | ||
|
|
dc66321a72 | ||
|
|
01baa46b36 | ||
|
|
dd0acfb628 | ||
|
|
ab14538c28 | ||
|
|
331287bfb6 | ||
|
|
02cfe180db | ||
|
|
b681737dce | ||
|
|
99011d2514 | ||
|
|
8aa4749a3e | ||
|
|
359582118c | ||
|
|
75c8feb42a | ||
|
|
fcfd56c00c | ||
|
|
d598e19da4 | ||
|
|
23a07f15ab | ||
|
|
a3e34a9a1a | ||
|
|
4991965ed9 | ||
|
|
7560a07458 | ||
|
|
ee3b126a04 | ||
|
|
9c4af81b2f | ||
|
|
83878ceeda | ||
|
|
6206b4a636 | ||
|
|
da151a1ff4 | ||
|
|
dd686ed6dd | ||
|
|
93908bfa54 | ||
|
|
e28b49317e | ||
|
|
6e7a96e762 | ||
|
|
a026ead29d | ||
|
|
e1b8086c40 | ||
|
|
9847d4558b | ||
|
|
eef1e4eca9 | ||
|
|
b7dd116136 | ||
|
|
8c6d096fa7 | ||
|
|
81d5132ac6 | ||
|
|
4c51a62b2a | ||
|
|
e587735050 | ||
|
|
808e42f9b5 | ||
|
|
fd62b4e9b3 | ||
|
|
dc1dd9aa3f | ||
|
|
67b1f36a38 | ||
|
|
9de5607394 | ||
|
|
6e994d57c6 | ||
|
|
90c8f674e1 | ||
|
|
3068a9e16a | ||
|
|
6c972c1e19 | ||
|
|
807e3b2369 | ||
|
|
f9c58b4912 | ||
|
|
3fc2a55468 | ||
|
|
8d9d4c9089 | ||
|
|
76d1d9b167 | ||
|
|
652eb87bcc | ||
|
|
028cf2a00c | ||
|
|
c04e8b6ead | ||
|
|
08c2d574ce | ||
|
|
cf8720e4dd | ||
|
|
2bca2b2b7e | ||
|
|
f9c8ed74e1 | ||
|
|
1a488dd2ad | ||
|
|
b58f37011d | ||
|
|
b1f6dd58f3 | ||
|
|
d634f2c9ae | ||
|
|
90b1033870 | ||
|
|
d20ed9ec0c | ||
|
|
d847ab6fa1 | ||
|
|
94272f76d4 | ||
|
|
56558023a8 | ||
|
|
b56230e3a7 | ||
|
|
5e98b1335a | ||
|
|
36e03b75b2 | ||
|
|
a371b9255c | ||
|
|
01e7a7ff26 | ||
|
|
7f4faf8696 | ||
|
|
2a14e3e085 | ||
|
|
b7521d00c7 | ||
|
|
7a77e563c9 | ||
|
|
bb119518c4 | ||
|
|
d9048fdfda | ||
|
|
4f00a22921 | ||
|
|
4ce5f49144 | ||
|
|
3d5d812e05 | ||
|
|
2df96d3a9b | ||
|
|
b344db021b | ||
|
|
2dabff5db1 | ||
|
|
e4a31f065d | ||
|
|
149e14e85d | ||
|
|
38339651cc | ||
|
|
652cc9fecc | ||
|
|
13c2581cbd | ||
|
|
6f5f182aef | ||
|
|
393323ba91 | ||
|
|
5a6ffbf916 | ||
|
|
6de667c125 | ||
|
|
ba03ac29fd | ||
|
|
3691dcc9e0 | ||
|
|
8c976445c0 | ||
|
|
586b0ddc5f | ||
|
|
cb0d4f2200 | ||
|
|
65f884616a | ||
|
|
bc139cb0e2 | ||
|
|
b0bb03b1d1 | ||
|
|
6a4de66edb | ||
|
|
0982378f96 | ||
|
|
8d4a9a959f | ||
|
|
5d0d302ed5 | ||
|
|
fd2730eaa7 | ||
|
|
ef2d6f7949 | ||
|
|
b9c07a7151 | ||
|
|
e6a3dd4c34 | ||
|
|
8697bc8b11 | ||
|
|
a0d7bd0726 | ||
|
|
1a8131f17f | ||
|
|
af62946651 | ||
|
|
351087d4e1 | ||
|
|
a268be69fe | ||
|
|
a9a1982943 | ||
|
|
850160d120 | ||
|
|
8dc7256a4a | ||
|
|
02fdb83282 | ||
|
|
df3fdf3758 | ||
|
|
11c559ee6d | ||
|
|
5e6a202ce0 | ||
|
|
f31dbe6c2a | ||
|
|
f18cdd20ce | ||
|
|
08e2048eeb | ||
|
|
a8156d2fa6 | ||
|
|
e593d5da34 | ||
|
|
b72145d4aa | ||
|
|
7cdd026e04 | ||
|
|
8e395d6715 | ||
|
|
0868f466b6 | ||
|
|
7144b4b271 | ||
|
|
b99d2363b9 | ||
|
|
0131900d79 | ||
|
|
2cd2a8b9b7 | ||
|
|
f918149430 | ||
|
|
7ecec19232 | ||
|
|
0ee297504c | ||
|
|
e680c3a478 | ||
|
|
5a026eaf57 | ||
|
|
873382eaa9 | ||
|
|
07def16ff6 | ||
|
|
fdc4b5f769 | ||
|
|
8ad9f99606 | ||
|
|
adb82a8414 | ||
|
|
d36f97aa6d | ||
|
|
d2728bea27 | ||
|
|
d67e23890b | ||
|
|
597eb46c47 | ||
|
|
4b4f4c8a30 | ||
|
|
467ba09720 | ||
|
|
1cfc9f1c5a | ||
|
|
3ba9f1e957 | ||
|
|
b9b8b6d5be | ||
|
|
f5aa3dc76f | ||
|
|
9dcf3ae934 | ||
|
|
7629f497f5 | ||
|
|
39b6dd70e4 | ||
|
|
c8e5b0ac00 | ||
|
|
d8a1699691 | ||
|
|
95dbd1e4cc | ||
|
|
eaa359d70c | ||
|
|
37524ebb37 | ||
|
|
750f502ac6 | ||
|
|
aa0b03064a | ||
|
|
04a1fa1cf0 | ||
|
|
7951e25319 | ||
|
|
d7c42cd2aa | ||
|
|
4f6cb72a3b | ||
|
|
ee6adf5ca5 | ||
|
|
a2fffd64fd | ||
|
|
18e0175412 | ||
|
|
890486e353 | ||
|
|
406ee30b1c | ||
|
|
ccced8c9f7 | ||
|
|
13c0639fa9 | ||
|
|
c59ae5205d | ||
|
|
a0df88dd71 | ||
|
|
b813988dd6 | ||
|
|
db1f4db2ff | ||
|
|
1ce0cbc9bb | ||
|
|
cb73261283 | ||
|
|
d073306f05 | ||
|
|
e99d2876ce | ||
|
|
9e09134f27 | ||
|
|
1826880edf | ||
|
|
8525855fe2 | ||
|
|
17b91ce812 | ||
|
|
5ebe68c9c9 | ||
|
|
861e050c02 | ||
|
|
5b72076895 | ||
|
|
98ffa0c368 | ||
|
|
9e07526f7e | ||
|
|
5e0a8eb914 | ||
|
|
814990f75d | ||
|
|
35b2dbec59 | ||
|
|
68106108ee | ||
|
|
216dfbccec | ||
|
|
f8aa623c9a | ||
|
|
3d30cfbb13 | ||
|
|
5f1303ffd1 | ||
|
|
d300e35b6a | ||
|
|
aa8e9758d1 | ||
|
|
4604a80f72 | ||
|
|
54b0fe25f3 | ||
|
|
6dc1b1b102 | ||
|
|
01b018866d | ||
|
|
03d402c8c5 | ||
|
|
29f8d783c4 | ||
|
|
b14e82aae4 | ||
|
|
cc23fe4e2d | ||
|
|
22d86b859e | ||
|
|
35f42107bb | ||
|
|
017f0901da | ||
|
|
54c39edafd | ||
|
|
d0148b47d5 | ||
|
|
e4137a6876 | ||
|
|
356b0ab546 | ||
|
|
992273013f | ||
|
|
1e91be3efa | ||
|
|
907ccbfd22 | ||
|
|
49ff420b8b | ||
|
|
269169815c | ||
|
|
724b114c34 | ||
|
|
a5bc193411 | ||
|
|
2b321914f5 | ||
|
|
921693f494 | ||
|
|
374bb30eea | ||
|
|
0f2d38551f | ||
|
|
3ec2c7c03b | ||
|
|
51bf7c8848 | ||
|
|
14dfe1f9c5 | ||
|
|
88b3c1b7ac | ||
|
|
285e72616e | ||
|
|
4cadedaa5d | ||
|
|
27f87c7345 | ||
|
|
6f5137fc56 | ||
|
|
948bc41562 | ||
|
|
ab5060a947 | ||
|
|
23d5a77814 | ||
|
|
6d6a085b97 | ||
|
|
233d1a48af | ||
|
|
eddb1e35fc | ||
|
|
2c2266ce8c | ||
|
|
a28270f43a | ||
|
|
824f80f357 | ||
|
|
bec8de3faa | ||
|
|
f9681d49b6 | ||
|
|
3169df3769 | ||
|
|
1314f070f7 | ||
|
|
eba3b9f119 | ||
|
|
19b8c6022f | ||
|
|
587dde157f | ||
|
|
149ea99344 | ||
|
|
1e32e2ef46 | ||
|
|
565972d602 | ||
|
|
dab2bffc7b | ||
|
|
7385d7caec | ||
|
|
becb32a947 | ||
|
|
bcadf6b0fb | ||
|
|
75121767d3 | ||
|
|
8da25d5295 | ||
|
|
ed069c48d3 | ||
|
|
ac79bfb35f | ||
|
|
5595b2f862 | ||
|
|
f03bcb8c14 | ||
|
|
b8e1fa2478 | ||
|
|
a2529e953b | ||
|
|
d783ae7c7c | ||
|
|
2c66bd6508 | ||
|
|
aeb6b44ca6 | ||
|
|
cbb32e5ce9 | ||
|
|
5f13b24f80 | ||
|
|
aaa353ec91 | ||
|
|
c0281e8b4c | ||
|
|
fde05adbd6 | ||
|
|
e1fff811ee | ||
|
|
3171512f30 | ||
|
|
b87953e2af | ||
|
|
ab8607e01a | ||
|
|
6db8757281 | ||
|
|
8ff060c589 |
@@ -1,4 +0,0 @@
|
||||
# Contributing to Clan
|
||||
|
||||
<!-- Local file: docs/CONTRIBUTING.md -->
|
||||
Go to the Contributing guide at https://docs.clan.lol/guides/contributing/CONTRIBUTING
|
||||
@@ -13,8 +13,6 @@
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
# We need to use `mkForce` because we inherit from `test-install-machine`
|
||||
# which currently hardcodes `nixpkgs.hostPlatform`
|
||||
nixpkgs.hostPlatform = lib.mkForce system;
|
||||
|
||||
imports = [ self.nixosModules.test-flash-machine ];
|
||||
@@ -28,6 +26,9 @@
|
||||
{
|
||||
imports = [ self.nixosModules.test-install-machine-without-system ];
|
||||
|
||||
# We don't want our system to define any `vars` generators as these can't
|
||||
# be generated as the flake is inside `/nix/store`.
|
||||
clan.core.settings.state-version.enable = false;
|
||||
clan.core.vars.generators.test = lib.mkForce { };
|
||||
disko.devices.disk.main.preCreateHook = lib.mkForce "";
|
||||
|
||||
@@ -59,11 +60,11 @@
|
||||
pkgs.kbd.out
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.ConfigIniFiles
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.FileSlurp
|
||||
pkgs.bubblewrap
|
||||
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
|
||||
(import ../installation/facter-report.nix pkgs.hostPlatform.system)
|
||||
]
|
||||
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
@@ -87,7 +88,7 @@
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = lib.mkForce 3;
|
||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||
flake-registry = "";
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
system:
|
||||
builtins.fetchurl {
|
||||
url = "https://git.clan.lol/clan/test-fixtures/raw/commit/4a2bc56d886578124b05060d3fb7eddc38c019f8/nixos-vm-facter-json/${system}.json";
|
||||
sha256 =
|
||||
{
|
||||
aarch64-linux = "sha256:1rlfymk03rmfkm2qgrc8l5kj5i20srx79n1y1h4nzlpwaz0j7hh2";
|
||||
x86_64-linux = "sha256:16myh0ll2gdwsiwkjw5ba4dl23ppwbsanxx214863j7nvzx42pws";
|
||||
}
|
||||
.${system};
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
privateInputs,
|
||||
|
||||
...
|
||||
}:
|
||||
{
|
||||
@@ -14,26 +14,37 @@
|
||||
# you can get a new one by adding
|
||||
# client.fail("cat test-flake/machines/test-install-machine/facter.json >&2")
|
||||
# to the installation test.
|
||||
clan.machines.test-install-machine-without-system = {
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
imports = [
|
||||
self.nixosModules.test-install-machine-without-system
|
||||
];
|
||||
};
|
||||
|
||||
clan.machines.test-install-machine-with-system =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# https://git.clan.lol/clan/test-fixtures
|
||||
facter.reportPath = import ./facter-report.nix pkgs.hostPlatform.system;
|
||||
|
||||
clan.machines = {
|
||||
test-install-machine-without-system = {
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
imports = [ self.nixosModules.test-install-machine-without-system ];
|
||||
imports = [
|
||||
self.nixosModules.test-install-machine-without-system
|
||||
];
|
||||
};
|
||||
}
|
||||
// (lib.listToAttrs (
|
||||
lib.map (
|
||||
system:
|
||||
lib.nameValuePair "test-install-machine-${system}" {
|
||||
imports = [
|
||||
self.nixosModules.test-install-machine-without-system
|
||||
(
|
||||
if privateInputs ? test-fixtures then
|
||||
{
|
||||
facter.reportPath = privateInputs.test-fixtures + /nixos-vm-facter-json/${system}.json;
|
||||
}
|
||||
else
|
||||
{ nixpkgs.hostPlatform = system; }
|
||||
)
|
||||
];
|
||||
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
}
|
||||
) (lib.filter (lib.hasSuffix "linux") config.systems)
|
||||
));
|
||||
|
||||
flake.nixosModules = {
|
||||
test-install-machine-without-system =
|
||||
@@ -149,13 +160,12 @@
|
||||
closureInfo = pkgs.closureInfo {
|
||||
rootPaths = [
|
||||
privateInputs.clan-core-for-checks
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.initialRamdisk
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
||||
self.nixosConfigurations."test-install-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
|
||||
self.nixosConfigurations."test-install-machine-${pkgs.hostPlatform.system}".config.system.build.initialRamdisk
|
||||
self.nixosConfigurations."test-install-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.bash.drvPath
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
(import ./facter-report.nix pkgs.hostPlatform.system)
|
||||
]
|
||||
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
};
|
||||
@@ -205,7 +215,7 @@
|
||||
# Prepare test flake and Nix store
|
||||
flake_dir = prepare_test_flake(
|
||||
temp_dir,
|
||||
"${self.checks.x86_64-linux.clan-core-for-checks}",
|
||||
"${self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks}",
|
||||
"${closureInfo}"
|
||||
)
|
||||
|
||||
@@ -216,6 +226,22 @@
|
||||
"${../assets/ssh/privkey}"
|
||||
)
|
||||
|
||||
# Run clan install from host using port forwarding
|
||||
clan_cmd = [
|
||||
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
||||
"machines",
|
||||
"init-hardware-config",
|
||||
"--debug",
|
||||
"--flake", str(flake_dir),
|
||||
"--yes", "test-install-machine-without-system",
|
||||
"--host-key-check", "none",
|
||||
"--target-host", f"nonrootuser@localhost:{ssh_conn.host_port}",
|
||||
"-i", ssh_conn.ssh_key,
|
||||
"--option", "store", os.environ['CLAN_TEST_STORE']
|
||||
]
|
||||
subprocess.run(clan_cmd, check=True)
|
||||
|
||||
|
||||
# Run clan install from host using port forwarding
|
||||
clan_cmd = [
|
||||
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
||||
@@ -270,7 +296,7 @@
|
||||
# Prepare test flake and Nix store
|
||||
flake_dir = prepare_test_flake(
|
||||
temp_dir,
|
||||
"${self.checks.x86_64-linux.clan-core-for-checks}",
|
||||
"${self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks}",
|
||||
"${closureInfo}"
|
||||
)
|
||||
|
||||
|
||||
@@ -147,28 +147,11 @@ let
|
||||
];
|
||||
doCheck = false;
|
||||
};
|
||||
|
||||
# Common closure info
|
||||
closureInfo = pkgs.closureInfo {
|
||||
rootPaths = [
|
||||
self.checks.x86_64-linux.clan-core-for-checks
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.initialRamdisk
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.clan.deployment.file
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.bash.drvPath
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
]
|
||||
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
inherit
|
||||
target
|
||||
baseTestMachine
|
||||
nixosTestLib
|
||||
closureInfo
|
||||
;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.stdenvNoCC
|
||||
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
|
||||
(import ../installation/facter-report.nix pkgs.hostPlatform.system)
|
||||
]
|
||||
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
|
||||
@@ -29,32 +29,34 @@ nixosLib.runTest (
|
||||
{ nodes, ... }:
|
||||
''
|
||||
import subprocess
|
||||
from nixos_test_lib.nix_setup import setup_nix_in_nix # type: ignore[import-untyped]
|
||||
import tempfile
|
||||
from nixos_test_lib.nix_setup import setup_nix_in_nix
|
||||
|
||||
setup_nix_in_nix(None) # No closure info for this test
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
setup_nix_in_nix(temp_dir, None) # No closure info for this test
|
||||
|
||||
start_all()
|
||||
admin1.wait_for_unit("multi-user.target")
|
||||
peer1.wait_for_unit("multi-user.target")
|
||||
start_all()
|
||||
admin1.wait_for_unit("multi-user.target")
|
||||
peer1.wait_for_unit("multi-user.target")
|
||||
|
||||
# peer1 should have the 'hello' file
|
||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.not-a-secret.path}")
|
||||
# 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}"
|
||||
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 command
|
||||
result = subprocess.run(
|
||||
["${
|
||||
clan-core.packages.${hostPkgs.system}.clan-cli
|
||||
}/bin/clan", "machines", "list", "--flake", "${config.clan.test.flakeForSandbox}"],
|
||||
check=True
|
||||
)
|
||||
# Run clan command
|
||||
result = subprocess.run(
|
||||
["${
|
||||
clan-core.packages.${hostPkgs.system}.clan-cli
|
||||
}/bin/clan", "machines", "list", "--flake", "${config.clan.test.flakeForSandbox}"],
|
||||
check=True
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
roles.peer = {
|
||||
description = "A peer that uses the new-service to generate some files.";
|
||||
};
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
|
||||
@@ -34,7 +34,9 @@ nixosLib.runTest (
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
roles.peer = {
|
||||
description = "A peer that uses the new-service to generate some files.";
|
||||
};
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
|
||||
@@ -67,6 +67,15 @@
|
||||
];
|
||||
};
|
||||
|
||||
nix.settings = {
|
||||
flake-registry = "";
|
||||
# required for setting the `flake-registry`
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
|
||||
# Define the mounts that exist in the container to prevent them from being stopped
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
@@ -106,13 +115,13 @@
|
||||
let
|
||||
closureInfo = pkgs.closureInfo {
|
||||
rootPaths = [
|
||||
self.packages.${pkgs.system}.clan-cli
|
||||
self.checks.${pkgs.system}.clan-core-for-checks
|
||||
self.packages.${pkgs.hostPlatform.system}.clan-cli
|
||||
self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-update-machine.config.system.build.toplevel
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.bash.drvPath
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
(import ../installation/facter-report.nix pkgs.hostPlatform.system)
|
||||
pkgs.bubblewrap
|
||||
]
|
||||
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
};
|
||||
@@ -123,7 +132,7 @@
|
||||
imports = [ self.nixosModules.test-update-machine ];
|
||||
};
|
||||
extraPythonPackages = _p: [
|
||||
self.legacyPackages.${pkgs.system}.nixosTestLib
|
||||
self.legacyPackages.${pkgs.hostPlatform.system}.nixosTestLib
|
||||
];
|
||||
|
||||
testScript = ''
|
||||
@@ -145,7 +154,7 @@
|
||||
# Prepare test flake and Nix store
|
||||
flake_dir = prepare_test_flake(
|
||||
temp_dir,
|
||||
"${self.checks.x86_64-linux.clan-core-for-checks}",
|
||||
"${self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks}",
|
||||
"${closureInfo}"
|
||||
)
|
||||
(flake_dir / ".clan-flake").write_text("") # Ensure .clan-flake exists
|
||||
@@ -212,12 +221,13 @@
|
||||
[
|
||||
"${pkgs.nix}/bin/nix",
|
||||
"copy",
|
||||
"--from",
|
||||
f"{temp_dir}/store",
|
||||
"--to",
|
||||
"ssh://root@192.168.1.1",
|
||||
"--no-check-sigs",
|
||||
f"${self.packages.${pkgs.system}.clan-cli}",
|
||||
f"${self.packages.${pkgs.hostPlatform.system}.clan-cli}",
|
||||
"--extra-experimental-features", "nix-command flakes",
|
||||
"--from", f"{os.environ["TMPDIR"]}/store"
|
||||
],
|
||||
check=True,
|
||||
env={
|
||||
@@ -232,7 +242,7 @@
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
f"root@192.168.1.1",
|
||||
"${self.packages.${pkgs.system}.clan-cli}/bin/clan",
|
||||
"${self.packages.${pkgs.hostPlatform.system}.clan-cli}/bin/clan",
|
||||
"machines",
|
||||
"update",
|
||||
"--debug",
|
||||
@@ -260,7 +270,7 @@
|
||||
|
||||
# Run clan update command
|
||||
subprocess.run([
|
||||
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
||||
"${self.packages.${pkgs.hostPlatform.system}.clan-cli-full}/bin/clan",
|
||||
"machines",
|
||||
"update",
|
||||
"--debug",
|
||||
@@ -287,7 +297,7 @@
|
||||
|
||||
# Run clan update command with --build-host
|
||||
subprocess.run([
|
||||
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
||||
"${self.packages.${pkgs.hostPlatform.system}.clan-cli-full}/bin/clan",
|
||||
"machines",
|
||||
"update",
|
||||
"--debug",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/admin";
|
||||
manifest.description = "Convenient Administration for the Clan App";
|
||||
manifest.description = "Adds a root user with ssh access";
|
||||
manifest.categories = [ "Utility" ];
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the admin service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
allowedKeys = lib.mkOption {
|
||||
default = { };
|
||||
|
||||
@@ -9,7 +9,7 @@ inventory.instances = {
|
||||
};
|
||||
roles.client.machines."jon".settings = {
|
||||
destinations."storagebox" = {
|
||||
repo = "username@$hostname:/./borgbackup";
|
||||
repo = "username@hostname:/./borgbackup";
|
||||
rsh = ''ssh -oPort=23 -i /run/secrets/vars/borgbackup/borgbackup.ssh'';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# TODO: a client can only be in one instance, add constraint
|
||||
|
||||
roles.server = {
|
||||
|
||||
description = "A borgbackup server that stores the backups of clients.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -54,7 +54,7 @@
|
||||
authorizedKeys = [ (builtins.readFile (borgbackupIpMachinePath machineName)) ];
|
||||
# };
|
||||
# }) machinesWithKey;
|
||||
}) roles.client.machines;
|
||||
}) (roles.client.machines or { });
|
||||
in
|
||||
hosts;
|
||||
};
|
||||
@@ -62,6 +62,7 @@
|
||||
};
|
||||
|
||||
roles.client = {
|
||||
description = "A borgbackup client that backs up to all borgbackup server roles.";
|
||||
interface =
|
||||
{
|
||||
lib,
|
||||
@@ -187,7 +188,7 @@
|
||||
config.clan.core.vars.generators.borgbackup.files."borgbackup.ssh".path
|
||||
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=Yes";
|
||||
};
|
||||
}) (builtins.attrNames roles.server.machines);
|
||||
}) (builtins.attrNames (roles.server.machines or { }));
|
||||
in
|
||||
(builtins.listToAttrs destinations);
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "certificates";
|
||||
manifest.description = "Sets up a certificates internal to your Clan";
|
||||
manifest.description = "Sets up a PKI certificate chain using step-ca";
|
||||
manifest.categories = [ "Network" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.ca = {
|
||||
|
||||
description = "A certificate authority that issues and signs certificates for other machines.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -184,6 +184,7 @@
|
||||
|
||||
# Empty role, so we can add non-ca machins to the instance to trust the CA
|
||||
roles.default = {
|
||||
description = "A machine that trusts the CA and can get certificates issued by it.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -45,13 +45,15 @@ inventory = {
|
||||
# Add the default role to all machines, including `client`
|
||||
roles.default.tags.all = { };
|
||||
|
||||
# DNS server
|
||||
# DNS server queries to http://<name>.foo are resolved here
|
||||
roles.server.machines."dnsserver".settings = {
|
||||
ip = "192.168.1.2";
|
||||
tld = "foo";
|
||||
};
|
||||
|
||||
# First service
|
||||
# Registers http://one.foo will resolve to 192.168.1.3
|
||||
# underlying service runs on server01
|
||||
roles.default.machines."server01".settings = {
|
||||
ip = "192.168.1.3";
|
||||
services = [ "one" ];
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.server = {
|
||||
|
||||
description = "A DNS server that resolves services in the clan network.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -103,6 +103,7 @@
|
||||
};
|
||||
|
||||
roles.default = {
|
||||
description = "A machine that registers the 'server' role as resolver and registers services under the configured TLD in the resolver.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -101,6 +101,7 @@ in
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.admin = {
|
||||
description = "A data-mesher admin node that bootstraps the network and can sign new nodes into the network.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -177,6 +178,7 @@ in
|
||||
};
|
||||
|
||||
roles.signer = {
|
||||
description = "A data-mesher signer node that can sign new nodes into the network.";
|
||||
interface = sharedInterface;
|
||||
perInstance =
|
||||
{
|
||||
@@ -208,6 +210,7 @@ in
|
||||
};
|
||||
|
||||
roles.peer = {
|
||||
description = "A data-mesher peer node that connects to the network.";
|
||||
interface = sharedInterface;
|
||||
perInstance =
|
||||
{
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/dyndns";
|
||||
manifest.description = "A dynamic DNS service to update domain IPs";
|
||||
manifest.description = "A dynamic DNS service to auto update domain IPs";
|
||||
manifest.categories = [ "Network" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the dyndns service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,31 +2,34 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/emergency-access";
|
||||
manifest.description = "Set recovery password for emergency access to machine";
|
||||
manifest.description = "Set recovery password for emergency access to machine to debug boot issues";
|
||||
manifest.categories = [ "System" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default.perInstance = {
|
||||
nixosModule =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
boot.initrd.systemd.emergencyAccess =
|
||||
config.clan.core.vars.generators.emergency-access.files.password-hash.value;
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the emergency-access service";
|
||||
perInstance = {
|
||||
nixosModule =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
boot.initrd.systemd.emergencyAccess =
|
||||
config.clan.core.vars.generators.emergency-access.files.password-hash.value;
|
||||
|
||||
clan.core.vars.generators.emergency-access = {
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.mkpasswd
|
||||
pkgs.xkcdpass
|
||||
];
|
||||
files.password.deploy = false;
|
||||
files.password-hash.secret = false;
|
||||
clan.core.vars.generators.emergency-access = {
|
||||
runtimeInputs = [
|
||||
pkgs.coreutils
|
||||
pkgs.mkpasswd
|
||||
pkgs.xkcdpass
|
||||
];
|
||||
files.password.deploy = false;
|
||||
files.password-hash.secret = false;
|
||||
|
||||
script = ''
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > $out/password
|
||||
mkpasswd -s -m sha-512 < $out/password | tr -d "\n" > $out/password-hash
|
||||
'';
|
||||
script = ''
|
||||
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > $out/password
|
||||
mkpasswd -s -m sha-512 < $out/password | tr -d "\n" > $out/password-hash
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
manifest.categories = [ "System" ];
|
||||
|
||||
roles.default = {
|
||||
|
||||
description = "Placeholder role to apply the garage service";
|
||||
perInstance.nixosModule =
|
||||
{
|
||||
config,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# defined in this file directly (e.g. the "morning" role) or split up into a
|
||||
# separate file (e.g. the "evening" role)
|
||||
roles.morning = {
|
||||
description = "A morning greeting machine";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -67,6 +68,7 @@
|
||||
# the interface here, so we can see all settings of the service in one place,
|
||||
# but you can also move it to the respective file
|
||||
roles.evening = {
|
||||
description = "An evening greeting machine";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -6,5 +6,7 @@
|
||||
manifest.categories = [ "Utility" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = { };
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the importer service";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/internet";
|
||||
manifest.description = "direct access (or via ssh jumphost) to machines";
|
||||
manifest.description = "Part of the clan networking abstraction to define how to reach machines from outside the clan network over the internet, if defined has the highest priority";
|
||||
manifest.categories = [
|
||||
"System"
|
||||
"Network"
|
||||
];
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the internet service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "localbackup";
|
||||
manifest.description = "Automatically backups current machine to local directory.";
|
||||
manifest.description = "Automatically backups current machine to local directory or a mounted drive.";
|
||||
manifest.categories = [ "System" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the localbackup service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
manifest.categories = [ "Social" ];
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the matrix-synapse service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.telegraf = {
|
||||
description = "Placeholder role to apply the telegraf monitoring agent";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/mycelium";
|
||||
manifest.description = "End-2-end encrypted IPv6 overlay network";
|
||||
manifest.description = "End-2-end encrypted P2P IPv6 overlay network";
|
||||
manifest.categories = [
|
||||
"System"
|
||||
"Network"
|
||||
];
|
||||
|
||||
roles.peer = {
|
||||
description = "A peer in the mycelium network";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
];
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the packages service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -1,36 +1,91 @@
|
||||
The `sshd` Clan service manages SSH to make it easy to securely access your machines over the internet. The service uses `vars` to store the SSH host keys for each machine to ensure they remain stable across deployments.
|
||||
# Clan service: sshd
|
||||
What it does
|
||||
- Generates and persists SSH host keys via `vars`.
|
||||
- Optionally issues CA‑signed host certificates for servers.
|
||||
- Installs the `server` CA public key into `clients` `known_hosts` for TOFU‑less verification.
|
||||
|
||||
`sshd` also generates SSH certificates for both servers and clients allowing for certificate-based authentication for SSH.
|
||||
|
||||
The service also disables password-based authentication over SSH, to access your machines you'll need to use public key authentication or certificate-based authentication.
|
||||
When to use it
|
||||
- Zero‑TOFU SSH for dynamic fleets: admins/CI can connect to frequently rebuilt hosts (e.g., server-1.example.com) without prompts or per‑host `known_hosts` churn.
|
||||
|
||||
## Usage
|
||||
Roles
|
||||
- Server: runs sshd, presents a CA‑signed host certificate for `<machine>.<domain>`.
|
||||
- Client: trusts the CA for the given domains to verify servers’ certificates.
|
||||
Tip: assign both roles to a machine if it should both present a cert and verify others.
|
||||
|
||||
Quick start (with host certificates)
|
||||
Useful if you never want to get a prompt about trusting the ssh fingerprint.
|
||||
```nix
|
||||
{
|
||||
inventory.instances = {
|
||||
sshd-with-certs = {
|
||||
module = { name = "sshd"; input = "clan-core"; };
|
||||
# Servers present certificates for <machine>.example.com
|
||||
roles.server.tags.all = { };
|
||||
roles.server.settings = {
|
||||
certificate.searchDomains = [ "example.com" ];
|
||||
# Optional: also add RSA host keys
|
||||
# hostKeys.rsa.enable = true;
|
||||
};
|
||||
# Clients trust the CA for *.example.com
|
||||
roles.client.tags.all = { };
|
||||
roles.client.settings = {
|
||||
certificate.searchDomains = [ "example.com" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Basic: only add persistent host keys (ed25519), no certificates
|
||||
Useful if you want to get an ssh "trust this server" prompt once and then never again.
|
||||
```nix
|
||||
{
|
||||
inventory.instances = {
|
||||
# By default this service only generates ed25519 host keys
|
||||
sshd-basic = {
|
||||
module = {
|
||||
name = "sshd";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.server.tags.all = { };
|
||||
roles.client.tags.all = { };
|
||||
};
|
||||
|
||||
# Also generate RSA host keys for all servers
|
||||
sshd-with-rsa = {
|
||||
module = {
|
||||
name = "sshd";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.server.tags.all = { };
|
||||
roles.server.settings = {
|
||||
hostKeys.rsa.enable = true;
|
||||
};
|
||||
roles.client.tags.all = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Example: selective trust per environment
|
||||
Admins should trust only production; CI should trust prod and staging. Servers are reachable under both domains.
|
||||
```nix
|
||||
{
|
||||
inventory.instances = {
|
||||
sshd-env-scoped = {
|
||||
module = { name = "sshd"; input = "clan-core"; };
|
||||
|
||||
# Servers present certs for both prod and staging FQDNs
|
||||
roles.server.tags.all = { };
|
||||
roles.server.settings = {
|
||||
certificate.searchDomains = [ "prod.example.com" "staging.example.com" ];
|
||||
};
|
||||
|
||||
# Admin laptop: trust prod only
|
||||
roles.client.machines."admin-laptop".settings = {
|
||||
certificate.searchDomains = [ "prod.example.com" ];
|
||||
};
|
||||
|
||||
# CI runner: trust prod and staging
|
||||
roles.client.machines."ci-runner-1".settings = {
|
||||
certificate.searchDomains = [ "prod.example.com" "staging.example.com" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
- Admin -> server1.prod.example.com: zero‑TOFU (verified via cert).
|
||||
- Admin -> server1.staging.example.com: falls back to TOFU (or is blocked by policy).
|
||||
- CI -> either prod or staging: zero‑TOFU for both.
|
||||
Note: server and client searchDomains don’t have to be identical; they only need to overlap for the hostnames you actually use.
|
||||
|
||||
Notes
|
||||
- Connect using a name that matches a cert principal (e.g., `server1.example.com`); wildcards are not allowed inside the certificate.
|
||||
- CA private key stays in `vars` (not deployed); only the CA public key is distributed.
|
||||
- Logins still require your user SSH keys on the server (passwords are disabled).
|
||||
@@ -10,6 +10,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.client = {
|
||||
description = "Installs the SSH CA public key into known_hosts for the configured domains, so this machine can verify servers’ host certificates without TOFU prompts.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -38,7 +39,6 @@
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
clan.core.vars.generators.openssh-ca = lib.mkIf (settings.certificate.searchDomains != [ ]) {
|
||||
share = true;
|
||||
files.id_ed25519.deploy = false;
|
||||
@@ -64,11 +64,12 @@
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
description = "Runs sshd with persistent host keys and (if certificate.searchDomains is set) a CA‑signed host certificate for <machine>.<domain>, enabling TOFU‑less verification by clients that trust the CA.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options = {
|
||||
hostKeys.rsa.enable = lib.mkEnableOption "Generate RSA host key";
|
||||
hostKeys.rsa.enable = lib.mkEnableOption "generating a RSA host key";
|
||||
|
||||
certificate = {
|
||||
searchDomains = lib.mkOption {
|
||||
@@ -96,9 +97,7 @@
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
clan.core.vars.generators = {
|
||||
|
||||
openssh-ca = lib.mkIf (settings.certificate.searchDomains != [ ]) {
|
||||
share = true;
|
||||
files.id_ed25519.deploy = false;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
Now the folder `~/syncthing/documents` will be shared with all your machines.
|
||||
Now the folder `~/syncthing/documents` will be shared and kept in sync with all your machines.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.peer = {
|
||||
description = "A peer in the syncthing cluster that syncs files with other peers.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/tor";
|
||||
manifest.description = "Onion routing, use Hidden services to connect your machines";
|
||||
manifest.description = "Part of the clan networking abstraction to define how to reach machines through the Tor network, if used has the lowest priority";
|
||||
manifest.categories = [
|
||||
"System"
|
||||
"Network"
|
||||
];
|
||||
|
||||
roles.client = {
|
||||
description = ''
|
||||
Enables a continuosly running Tor proxy on the machine, allowing access to other machines via the Tor network.
|
||||
If not enabled, a Tor proxy will be started automatically when required.
|
||||
'';
|
||||
perInstance =
|
||||
{
|
||||
...
|
||||
@@ -31,6 +35,7 @@
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
description = "Sets up a Tor onion service for the machine, thus making it reachable over Tor.";
|
||||
# interface =
|
||||
# { lib, ... }:
|
||||
# {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
|
||||
description = "Placeholder role to apply the trusted-nix-caches service";
|
||||
perInstance =
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the user service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
21
clanServices/wifi/README.md
Normal file
21
clanServices/wifi/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
This module allows you to pre-configure WiFi networks for automatic connection.
|
||||
Each attribute in `settings.network` serves as an internal identifier, not the actual SSID.
|
||||
After defining your networks, you will be prompted for the SSID and password for each one.
|
||||
|
||||
This module leverages NetworkManager for managing connections.
|
||||
|
||||
```nix
|
||||
instances = {
|
||||
wifi = {
|
||||
module.name = "wifi";
|
||||
module.input = "clan-core";
|
||||
|
||||
roles.default = {
|
||||
machines."jon" = {
|
||||
settings.networks.home = { };
|
||||
settings.networks.work = { keyMgmt = "wpa-eap"; };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
@@ -9,8 +9,11 @@ in
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "wifi";
|
||||
manifest.description = "Pre configure wifi networks to connect to";
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the wifi service";
|
||||
interface = {
|
||||
options.networks = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
@@ -42,7 +45,18 @@ in
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = "Wifi networks to predefine";
|
||||
example = {
|
||||
home = { };
|
||||
guest = {
|
||||
autoConnect = false;
|
||||
keyMgmt = "wpa-eap";
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
List of wifi networks to configure for connection.
|
||||
Each attribute name is an internal identifier (not the SSID).
|
||||
For each network, you will be prompted to enter the SSID and password as secrets.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ in
|
||||
|
||||
# Peer options and configuration
|
||||
roles.peer = {
|
||||
description = "A peer that connects to one or more controllers.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -261,6 +262,7 @@ in
|
||||
|
||||
# Controller options and configuration
|
||||
roles.controller = {
|
||||
description = "A controller that routes peer traffic. Must be publicly reachable.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
manifest.description = "Yggdrasil encrypted IPv6 routing overlay network";
|
||||
|
||||
roles.default = {
|
||||
description = "Placeholder role to apply the yggdrasil service";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -88,7 +89,7 @@
|
||||
enable = true;
|
||||
openMulticastPort = true;
|
||||
# We don't need this option, because we persist our keys with
|
||||
# vars by ourselfs. This option creates an unnessesary additional
|
||||
# vars by ourselves. This option creates an unnecesary additional
|
||||
# systemd service to save/load the keys and should be removed
|
||||
# from the NixOS module entirely, as it can be replaced by the
|
||||
# (at the time of writing undocumented) PrivateKeyPath= setting.
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/zerotier";
|
||||
manifest.description = "Configuration of the secure and efficient Zerotier VPN";
|
||||
manifest.description = "Zerotier Mesh VPN Service for secure P2P networking between machines";
|
||||
manifest.categories = [ "Utility" ];
|
||||
manifest.readme = builtins.readFile ./README.md;
|
||||
|
||||
roles.peer = {
|
||||
description = "A peer that connects to your private Zerotier network.";
|
||||
perInstance =
|
||||
{
|
||||
instanceName,
|
||||
@@ -51,6 +52,7 @@
|
||||
};
|
||||
|
||||
roles.moon = {
|
||||
description = "A moon acts as a relay node to connect other nodes in the zerotier network that are not publicly reachable. Each moon must be publicly reachable.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -101,6 +103,7 @@
|
||||
};
|
||||
|
||||
roles.controller = {
|
||||
description = "Manages network membership and is responsible for admitting new peers to your Zerotier network.";
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
67
devFlake/flake.lock
generated
67
devFlake/flake.lock
generated
@@ -3,10 +3,10 @@
|
||||
"clan-core-for-checks": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1756166884,
|
||||
"narHash": "sha256-skg4rwpbCjhpLlrv/Pndd43FoEgrJz98WARtGLhCSzo=",
|
||||
"lastModified": 1759795610,
|
||||
"narHash": "sha256-YFOK+aoJjWLfMHj2spvrQIe0ufIsv6P8o44NqoFPwp0=",
|
||||
"ref": "main",
|
||||
"rev": "f7414d7e6e58709af27b6fe16eb530278e81eaaf",
|
||||
"rev": "0de79962eacfe6f09d7aabca2a7305deef4fde0c",
|
||||
"shallow": true,
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/clan-core"
|
||||
@@ -18,6 +18,27 @@
|
||||
"url": "https://git.clan.lol/clan/clan-core"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"test-fixtures",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1741352980,
|
||||
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": [
|
||||
@@ -84,11 +105,11 @@
|
||||
},
|
||||
"nixpkgs-dev": {
|
||||
"locked": {
|
||||
"lastModified": 1758573205,
|
||||
"narHash": "sha256-0ybDco+HjG5h46wx7ww4JIyg3y/mBDgkMCVX/Ua0e/Q=",
|
||||
"lastModified": 1759794031,
|
||||
"narHash": "sha256-Zruni/00BlDHSWVJf3mb0o+OHnxIvJNuXkPloY9c+PU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "803b1683f562edc00665874bf98c1aad0b111482",
|
||||
"rev": "09c221b2f0726da85b124efb60a1d123971dfa08",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -107,11 +128,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758272005,
|
||||
"narHash": "sha256-1u3xTH+3kaHhztPmWtLAD8LF5pTYLR2CpsPFWTFnVtQ=",
|
||||
"lastModified": 1758662783,
|
||||
"narHash": "sha256-igrxT+/MnmcftPOHEb+XDwAMq3Xg1Xy7kVYQaHhPlAg=",
|
||||
"owner": "NuschtOS",
|
||||
"repo": "search",
|
||||
"rev": "aa975a3757f28ce862812466c5848787b868e116",
|
||||
"rev": "7d4c0fc4ffe3bd64e5630417162e9e04e64b27a4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -127,6 +148,7 @@
|
||||
"nixpkgs-dev": "nixpkgs-dev",
|
||||
"nuschtos": "nuschtos",
|
||||
"systems": "systems_2",
|
||||
"test-fixtures": "test-fixtures",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
@@ -160,16 +182,37 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"test-fixtures": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": [
|
||||
"nixpkgs-dev"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742806412,
|
||||
"narHash": "sha256-ZoAN0/sHEHY+ymJnkdkBAuZ/6sc66RYR4xMHpLf7++E=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "4a2bc56d886578124b05060d3fb7eddc38c019f8",
|
||||
"revCount": 2,
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/test-fixtures"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/test-fixtures"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": []
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758206697,
|
||||
"narHash": "sha256-/DbPkh6PZOgfueCbs3uzlk4ASU2nPPsiVWhpMCNkAd0=",
|
||||
"lastModified": 1758728421,
|
||||
"narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "128222dc911b8e2e18939537bed1762b7f3a04aa",
|
||||
"rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -18,5 +18,8 @@
|
||||
inputs.clan-core-for-checks.url = "git+https://git.clan.lol/clan/clan-core?ref=main&shallow=1";
|
||||
inputs.clan-core-for-checks.flake = false;
|
||||
|
||||
inputs.test-fixtures.url = "git+https://git.clan.lol/clan/test-fixtures";
|
||||
inputs.test-fixtures.inputs.nixpkgs.follows = "nixpkgs-dev";
|
||||
|
||||
outputs = inputs: inputs;
|
||||
}
|
||||
|
||||
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/site/reference
|
||||
/site/services/official
|
||||
/site/static
|
||||
/site/options
|
||||
/site/openapi.json
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Contributing to Clan
|
||||
|
||||
|
||||
**Continuous Integration (CI)**: Each pull request gets automatically tested by gitea. If any errors are detected, it will block pull requests until they're resolved.
|
||||
|
||||
**Dependency Management**: We use the [Nix package manager](https://nixos.org/) to manage dependencies and ensure reproducibility, making your development process more robust.
|
||||
@@ -10,25 +9,27 @@
|
||||
- Linux
|
||||
- macOS
|
||||
|
||||
# Getting Started with the Development Environment
|
||||
## Getting Started with the Development Environment
|
||||
|
||||
Let's get your development environment up and running:
|
||||
|
||||
1. **Install Nix Package Manager**:
|
||||
|
||||
- You can install the Nix package manager by either [downloading the Nix installer](https://github.com/DeterminateSystems/nix-installer/releases) or running this command:
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
|
||||
```
|
||||
|
||||
1. **Install direnv**:
|
||||
```bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
|
||||
```
|
||||
|
||||
2. **Install direnv**:
|
||||
|
||||
- To automatically setup a devshell on entering the directory
|
||||
```bash
|
||||
nix profile install nixpkgs#nix-direnv-flakes nixpkgs#direnv
|
||||
```
|
||||
|
||||
1. **Add direnv to your shell**:
|
||||
```bash
|
||||
nix profile install nixpkgs#nix-direnv-flakes nixpkgs#direnv
|
||||
```
|
||||
|
||||
3. **Add direnv to your shell**:
|
||||
|
||||
- Direnv needs to [hook into your shell](https://direnv.net/docs/hook.html) to work.
|
||||
You can do this by executing following command. The example below will setup direnv for `zsh` and `bash`
|
||||
@@ -37,34 +38,43 @@ Let's get your development environment up and running:
|
||||
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc && echo 'eval "$(direnv hook bash)"' >> ~/.bashrc && eval "$SHELL"
|
||||
```
|
||||
|
||||
1. **Allow the devshell**
|
||||
4. **Allow the devshell**
|
||||
- Go to `clan-core/pkgs/clan-cli` and do a `direnv allow` to setup the necessary development environment to execute the `clan` command
|
||||
|
||||
1. **Create a Gitea Account**:
|
||||
5. **Create a Gitea Account**
|
||||
|
||||
- Register an account on https://git.clan.lol
|
||||
- Fork the [clan-core](https://git.clan.lol/clan/clan-core) repository
|
||||
- Clone the repository and navigate to it
|
||||
- Add a new remote called upstream:
|
||||
```bash
|
||||
git remote add upstream gitea@git.clan.lol:clan/clan-core.git
|
||||
```
|
||||
1. **Allow .envrc**:
|
||||
- Add a new remote called upstream
|
||||
|
||||
```bash
|
||||
git remote add upstream gitea@git.clan.lol:clan/clan-core.git
|
||||
```
|
||||
|
||||
7. **Allow .envrc**
|
||||
|
||||
- When you enter the directory, you'll receive an error message like this:
|
||||
```bash
|
||||
direnv: error .envrc is blocked. Run `direnv allow` to approve its content
|
||||
```
|
||||
|
||||
```bash
|
||||
direnv: error .envrc is blocked. Run `direnv allow` to approve its content
|
||||
```
|
||||
|
||||
- Execute `direnv allow` to automatically execute the shell script `.envrc` when entering the directory.
|
||||
|
||||
1. **(Optional) Install Git Hooks**:
|
||||
8. **(Optional) Install Git Hooks**
|
||||
|
||||
- To syntax check your code you can run:
|
||||
```bash
|
||||
nix fmt
|
||||
```
|
||||
|
||||
```bash
|
||||
nix fmt
|
||||
```
|
||||
|
||||
- To make this automatic install the git hooks
|
||||
```bash
|
||||
./scripts/pre-commit
|
||||
```
|
||||
|
||||
```bash
|
||||
./scripts/pre-commit
|
||||
```
|
||||
|
||||
## Related Projects
|
||||
|
||||
@@ -73,7 +83,7 @@ Let's get your development environment up and running:
|
||||
- **Nixos Anywhere**: [nixos-anywhere](https://github.com/nix-community/nixos-anywhere)
|
||||
- **Disko**: [disko](https://github.com/nix-community/disko)
|
||||
|
||||
## Fixing Bugs or Adding Features in Clan-CLI
|
||||
### Override related projects for local development
|
||||
|
||||
If you have a bug fix or feature that involves a related project, clone the relevant repository and replace its invocation in your local setup.
|
||||
|
||||
@@ -102,10 +112,10 @@ run(
|
||||
|
||||
```
|
||||
|
||||
The <path_to_local_src> doesn't need to be a local path, it can be any valid [flakeref](https://nix.dev/manual/nix/2.26/command-ref/new-cli/nix3-flake.html#flake-references).
|
||||
The `<path_to_local_src>` doesn't need to be a local path, it can be any valid [flakeref](https://nix.dev/manual/nix/2.26/command-ref/new-cli/nix3-flake.html#flake-references).
|
||||
And thus can point to test already opened PRs for example.
|
||||
|
||||
# Standards
|
||||
## Standards
|
||||
|
||||
- Every new module name should be in kebab-case.
|
||||
- Every fact definition, where possible should be in kebab-case.
|
||||
|
||||
129
docs/mkdocs.yml
129
docs/mkdocs.yml
@@ -47,25 +47,24 @@ exclude_docs: |
|
||||
nav:
|
||||
- Getting Started:
|
||||
- Overview: index.md
|
||||
- Creating Your First Clan: guides/getting-started/index.md
|
||||
- Add Machines: guides/getting-started/add-machines.md
|
||||
- Add User: guides/getting-started/add-user.md
|
||||
- Add Services: guides/getting-started/add-services.md
|
||||
- getting-started/creating-your-first-clan.md
|
||||
- getting-started/add-machines.md
|
||||
- getting-started/add-users.md
|
||||
- getting-started/add-services.md
|
||||
- Deploy to Physical Machine:
|
||||
- Create USB Installer: guides/getting-started/create-installer.md
|
||||
- Deploy Physical Machine: guides/getting-started/hardware-report-physical.md
|
||||
- Deploy to Virtual Machine: guides/getting-started/hardware-report-virtual.md
|
||||
- Configure Disk Config: guides/getting-started/choose-disk.md
|
||||
- Update Machine: guides/getting-started/update.md
|
||||
- Continuous Integration: guides/getting-started/flake-check.md
|
||||
- Convert Existing NixOS Config: guides/getting-started/convert-flake.md
|
||||
- getting-started/deploy-to-physical-machine/flash-installer.md
|
||||
- getting-started/deploy-to-physical-machine/install-machine.md
|
||||
- getting-started/deploy-to-virtual-machine.md
|
||||
- getting-started/configure-disk.md
|
||||
- getting-started/update-machines.md
|
||||
- getting-started/continuous-integration.md
|
||||
- getting-started/convert-existing-NixOS-configuration.md
|
||||
- Guides:
|
||||
- Inventory:
|
||||
- Introduction to Inventory: guides/inventory/inventory.md
|
||||
- File Autoincludes: guides/inventory/autoincludes.md
|
||||
|
||||
- Clan Services:
|
||||
- Inventory Guide: guides/inventory/clanServices.md
|
||||
- Services:
|
||||
- Introduction to Services: guides/services/introduction-to-services.md
|
||||
- Author Your Own Service: guides/services/community.md
|
||||
- Vars:
|
||||
- Introduction to Vars: guides/vars/vars-overview.md
|
||||
@@ -76,37 +75,40 @@ nav:
|
||||
- Sops Backend:
|
||||
- Yubikeys & Age Plugins: guides/vars/sops/age-plugins.md
|
||||
- Managing Users (OLD): guides/secrets.md
|
||||
- Backups:
|
||||
- Introduction to Backups: guides/backups/backup-intro.md
|
||||
- Minimal Example: guides/backups/minimal-example.md
|
||||
- Digging Deeper: guides/backups/digging-deeper.md
|
||||
- Advanced Example: guides/backups/advanced-example.md
|
||||
- Networking:
|
||||
- Introduction to Networking: guides/networking/networking.md
|
||||
- Zerotier VPN: guides/networking/mesh-vpn.md
|
||||
- Disko Templates:
|
||||
- Community Disko Templates: guides/disko-templates/community.md
|
||||
- Backups:
|
||||
- Introduction to Backups: guides/backups.md
|
||||
- Nixpkgs Flake Input: guides/nixpkgs-flake-input/index.md
|
||||
- Flake-parts: guides/flake-parts.md
|
||||
- NixOS Rebuild: guides/nixos-rebuild.md
|
||||
- macOS:
|
||||
- Managing macOS Machines: guides/macos.md
|
||||
- macOS: guides/macos.md
|
||||
# Should be part of the respective sections above
|
||||
# machines, disko, clan
|
||||
- Templates: concepts/templates.md
|
||||
- Templates:
|
||||
- Introduction to Templates: concepts/templates.md
|
||||
- Community Disko Templates: guides/disko-templates/community.md
|
||||
- Migrations:
|
||||
- clan modules --> clan services: guides/migrations/migrate-inventory-services.md
|
||||
- Facts --> Vars: guides/migrations/migration-facts-vars.md
|
||||
- clan modules to clan services: guides/migrations/migrate-inventory-services.md
|
||||
- Facts to Vars: guides/migrations/migration-facts-vars.md
|
||||
- Disk id: guides/migrations/disk-id.md
|
||||
- Disk Encryption: guides/disk-encryption.md
|
||||
- Disable Secure Boot: guides/secure-boot.md
|
||||
- Contributing:
|
||||
- Hacking: guides/contributing/CONTRIBUTING.md
|
||||
- Advanced Debugging: guides/contributing/debugging.md
|
||||
- Testing: guides/contributing/testing.md
|
||||
- guides/contributing/CONTRIBUTING.md
|
||||
- guides/contributing/debugging.md
|
||||
- guides/contributing/testing.md
|
||||
|
||||
- Reference:
|
||||
- Overview: reference/index.md
|
||||
- Clan Options: reference/options/clan.md
|
||||
- Clan Inventory Options: reference/options/clan_inventory.md
|
||||
|
||||
- Clan Service API: reference/clanServices/clan-service-author-interface.md
|
||||
- Options:
|
||||
- reference/options/clan.md
|
||||
- reference/options/clan_inventory.md
|
||||
- reference/options/clan_service.md
|
||||
|
||||
- clan.core (Machine Options):
|
||||
- Overview: reference/clan.core/index.md
|
||||
@@ -140,41 +142,42 @@ nav:
|
||||
- HTTP API: api.md
|
||||
|
||||
- Decisions:
|
||||
- Architecture Decisions: decisions/README.md
|
||||
- 01-clanModules: decisions/01-ClanModules.md
|
||||
- 02-clan-api: decisions/02-clan-api.md
|
||||
- 03-adr-numbering-process: decisions/03-adr-numbering-process.md
|
||||
- 04-fetching-nix-from-python: decisions/04-fetching-nix-from-python.md
|
||||
- 05-deployment-parameters: decisions/05-deployment-parameters.md
|
||||
- Template: decisions/_template.md
|
||||
- decisions/Architecture-decisions.md
|
||||
- decisions/01-Clan-Modules.md
|
||||
- decisions/02-clan-as-library.md
|
||||
- decisions/03-adr-numbering-process.md
|
||||
- decisions/04-fetching-nix-from-python.md
|
||||
- decisions/05-deployment-parameters.md
|
||||
- decisions/template.md
|
||||
- Glossary: reference/glossary.md
|
||||
- Services:
|
||||
- Introduction to ClanServices: reference/clanServices/index.md
|
||||
- services/definition.md
|
||||
# Generated list from the list of official services
|
||||
- Official:
|
||||
- reference/clanServices/admin.md
|
||||
- reference/clanServices/borgbackup.md
|
||||
- reference/clanServices/certificates.md
|
||||
- reference/clanServices/coredns.md
|
||||
- reference/clanServices/data-mesher.md
|
||||
- reference/clanServices/dyndns.md
|
||||
- reference/clanServices/emergency-access.md
|
||||
- reference/clanServices/garage.md
|
||||
- reference/clanServices/hello-world.md
|
||||
- reference/clanServices/importer.md
|
||||
- reference/clanServices/localbackup.md
|
||||
- reference/clanServices/matrix-synapse.md
|
||||
- reference/clanServices/mycelium.md
|
||||
- reference/clanServices/monitoring.md
|
||||
- reference/clanServices/packages.md
|
||||
- reference/clanServices/sshd.md
|
||||
- reference/clanServices/syncthing.md
|
||||
- reference/clanServices/trusted-nix-caches.md
|
||||
- reference/clanServices/users.md
|
||||
- reference/clanServices/wifi.md
|
||||
- reference/clanServices/wireguard.md
|
||||
- reference/clanServices/yggdrasil.md
|
||||
- reference/clanServices/zerotier.md
|
||||
- Community: community/services/index.md
|
||||
- services/official/admin.md
|
||||
- services/official/borgbackup.md
|
||||
- services/official/certificates.md
|
||||
- services/official/coredns.md
|
||||
- services/official/data-mesher.md
|
||||
- services/official/dyndns.md
|
||||
- services/official/emergency-access.md
|
||||
- services/official/garage.md
|
||||
- services/official/hello-world.md
|
||||
- services/official/importer.md
|
||||
- services/official/localbackup.md
|
||||
- services/official/matrix-synapse.md
|
||||
- services/official/mycelium.md
|
||||
- services/official/monitoring.md
|
||||
- services/official/packages.md
|
||||
- services/official/sshd.md
|
||||
- services/official/syncthing.md
|
||||
- services/official/trusted-nix-caches.md
|
||||
- services/official/users.md
|
||||
- services/official/wifi.md
|
||||
- services/official/wireguard.md
|
||||
- services/official/yggdrasil.md
|
||||
- services/official/zerotier.md
|
||||
- services/community.md
|
||||
|
||||
- Search Clan Options: "/options"
|
||||
|
||||
@@ -230,7 +233,7 @@ extra:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/clan-lol/clan-core
|
||||
- icon: fontawesome/solid/rss
|
||||
link: /feed_rss_created.xml
|
||||
link: https://clan.lol/feed.xml
|
||||
|
||||
plugins:
|
||||
- search
|
||||
|
||||
@@ -44,13 +44,13 @@ pkgs.stdenv.mkDerivation {
|
||||
pushd docs
|
||||
|
||||
mkdir -p ./site/reference/cli
|
||||
cp -af ${module-docs}/* ./site/reference/
|
||||
cp -af ${module-docs}/services/* ./site/services/
|
||||
cp -af ${module-docs}/reference/* ./site/reference/
|
||||
cp -af ${clan-cli-docs}/* ./site/reference/cli/
|
||||
|
||||
mkdir -p ./site/reference/internal
|
||||
cp -af ${clan-lib-openapi} ./site/openapi.json
|
||||
|
||||
chmod -R +w ./site/reference
|
||||
chmod -R +w ./site
|
||||
echo "Generated API documentation in './site/reference/' "
|
||||
|
||||
rm -rf ./site/options
|
||||
|
||||
@@ -88,8 +88,12 @@
|
||||
;
|
||||
};
|
||||
devShells.docs = self'.packages.docs.overrideAttrs (_old: {
|
||||
nativeBuildInputs =
|
||||
self'.devShells.default.nativeBuildInputs ++ self'.packages.docs.nativeBuildInputs;
|
||||
nativeBuildInputs = [
|
||||
# Run: htmlproofer --disable-external
|
||||
pkgs.html-proofer
|
||||
]
|
||||
++ self'.devShells.default.nativeBuildInputs
|
||||
++ self'.packages.docs.nativeBuildInputs;
|
||||
shellHook = ''
|
||||
${self'.devShells.default.shellHook}
|
||||
git_root=$(git rev-parse --show-toplevel)
|
||||
@@ -113,5 +117,17 @@
|
||||
deploy-docs = pkgs.callPackage ./deploy-docs.nix { inherit (config.packages) docs; };
|
||||
inherit module-docs;
|
||||
};
|
||||
checks.docs-integrity =
|
||||
pkgs.runCommand "docs-integrity"
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.html-proofer ];
|
||||
}
|
||||
''
|
||||
# External links should be avoided in the docs, because they often break
|
||||
# and we cannot statically control them. Thus we disable checking them
|
||||
htmlproofer --disable-external ${self'.packages.docs}
|
||||
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ def produce_clan_core_docs() -> None:
|
||||
if module_type is not None and "submodule" not in module_type:
|
||||
continue
|
||||
core_outputs[indexfile] += (
|
||||
f" - [{submodule_name}](./{submodule_name}.md)\n"
|
||||
f" - [{submodule_name}](../../reference/clan.core/{submodule_name}.md)\n"
|
||||
)
|
||||
|
||||
core_outputs[indexfile] += options_head
|
||||
@@ -273,8 +273,10 @@ def produce_clan_core_docs() -> None:
|
||||
core_outputs[outfile] += output
|
||||
|
||||
for outfile, output in core_outputs.items():
|
||||
(Path(OUT) / outfile).parent.mkdir(parents=True, exist_ok=True)
|
||||
with (Path(OUT) / outfile).open("w") as of:
|
||||
(Path(OUT) / "reference" / outfile).parent.mkdir(
|
||||
parents=True, exist_ok=True
|
||||
)
|
||||
with (Path(OUT) / "reference" / outfile).open("w") as of:
|
||||
of.write(output)
|
||||
|
||||
|
||||
@@ -307,32 +309,6 @@ def produce_clan_service_docs() -> None:
|
||||
msg = f"Environment variables are not set correctly: $out={OUT}"
|
||||
raise ClanError(msg)
|
||||
|
||||
indexfile = Path(OUT) / "clanServices/index.md"
|
||||
indexfile.parent.mkdir(
|
||||
parents=True,
|
||||
exist_ok=True,
|
||||
)
|
||||
index = "# Clan Services\n\n"
|
||||
index += """
|
||||
**`clanServices`** are modular building blocks that simplify the configuration and orchestration of multi-host services.
|
||||
|
||||
Each `clanService`:
|
||||
|
||||
* Is a module of class **`clan.service`**
|
||||
* Can define **roles** (e.g., `client`, `server`)
|
||||
* Uses **`inventory.instances`** to configure where and how it is deployed
|
||||
|
||||
!!! Note
|
||||
`clanServices` are part of Clan's next-generation service model and are intended to replace `clanModules`.
|
||||
|
||||
See [Migration Guide](../../guides/migrations/migrate-inventory-services.md) for help on migrating.
|
||||
|
||||
Learn how to use `clanServices` in practice in the [Using clanServices guide](../../guides/inventory/clanServices.md).
|
||||
"""
|
||||
|
||||
with indexfile.open("w") as of:
|
||||
of.write(index)
|
||||
|
||||
with Path(CLAN_MODULES_VIA_SERVICE).open() as f3:
|
||||
service_links: dict[str, dict[str, dict[str, Any]]] = json.load(f3)
|
||||
|
||||
@@ -368,7 +344,7 @@ Learn how to use `clanServices` in practice in the [Using clanServices guide](..
|
||||
replace_prefix=f"clan.{module_name}",
|
||||
)
|
||||
|
||||
outfile = Path(OUT) / f"clanServices/{module_name}.md"
|
||||
outfile = Path(OUT) / "services/official" / f"{module_name}.md"
|
||||
outfile.parent.mkdir(
|
||||
parents=True,
|
||||
exist_ok=True,
|
||||
@@ -437,7 +413,7 @@ Typically needed by module authors to define roles, behavior and metadata for di
|
||||
# for option in options_tree.suboptions:
|
||||
output += options_docs_from_tree(options_tree, init_level=2)
|
||||
|
||||
outfile = Path(OUT) / "clanServices/clan-service-author-interface.md"
|
||||
outfile = Path(OUT) / "reference/options" / "clan_service.md"
|
||||
outfile.parent.mkdir(parents=True, exist_ok=True)
|
||||
with Path.open(outfile, "w") as of:
|
||||
of.write(output)
|
||||
@@ -452,10 +428,10 @@ def produce_inventory_docs() -> None:
|
||||
msg = f"Environment variables are not set correctly: $out={OUT}"
|
||||
raise ClanError(msg)
|
||||
|
||||
output = """# Inventory
|
||||
output = """# Inventory Submodule
|
||||
This provides an overview of the available options of the `inventory` model.
|
||||
|
||||
It can be set via the `inventory` attribute of the [`clan`](./clan.md#inventory) function, or via the [`clan.inventory`](./clan.md#inventory) attribute of flake-parts.
|
||||
It can be set via the `inventory` attribute of the [`clan`](../../reference/options/clan_inventory.md) function, or via the [`clan.inventory`](../../reference/options/clan_inventory.md) attribute of flake-parts.
|
||||
|
||||
"""
|
||||
# Inventory options are already included under the clan attribute
|
||||
@@ -479,7 +455,7 @@ It can be set via the `inventory` attribute of the [`clan`](./clan.md#inventory)
|
||||
for option in inventory_opt.suboptions:
|
||||
output += options_docs_from_tree(option, init_level=2)
|
||||
|
||||
outfile = Path(OUT) / "options/clan_inventory.md"
|
||||
outfile = Path(OUT) / "reference/options" / "clan_inventory.md"
|
||||
outfile.parent.mkdir(parents=True, exist_ok=True)
|
||||
with Path.open(outfile, "w") as of:
|
||||
of.write(output)
|
||||
@@ -497,8 +473,8 @@ def produce_clan_options_docs() -> None:
|
||||
output = """# Clan Options
|
||||
This provides an overview of the available options
|
||||
|
||||
Those can be set via [`clan-core.lib.clan`](./clan.md#inventory) function,
|
||||
or via the [`clan`](./clan.md) attribute of flake-parts.
|
||||
Those can be set via [`clan-core.lib.clan`](../../reference/options/clan.md) function,
|
||||
or via the [`clan`](../../reference/options/clan.md) attribute of flake-parts.
|
||||
|
||||
"""
|
||||
# Inventory options are already included under the clan attribute
|
||||
@@ -512,10 +488,16 @@ or via the [`clan`](./clan.md) attribute of flake-parts.
|
||||
# Exclude inventory options
|
||||
for option in clan_root_option.suboptions:
|
||||
if "inventory" in option.name:
|
||||
output += """## Inventory
|
||||
|
||||
Attribute: `inventory`
|
||||
|
||||
See: [Inventory Submodule](../../reference/options/clan_inventory.md)
|
||||
"""
|
||||
continue
|
||||
output += options_docs_from_tree(option, init_level=2)
|
||||
|
||||
outfile = Path(OUT) / "options/clan.md"
|
||||
outfile = Path(OUT) / "reference/options" / "clan.md"
|
||||
outfile.parent.mkdir(parents=True, exist_ok=True)
|
||||
with Path.open(outfile, "w") as of:
|
||||
of.write(output)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Clan service modules
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
@@ -1,5 +1,3 @@
|
||||
# Clan as library
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
@@ -1,5 +1,3 @@
|
||||
# ADR Numbering process
|
||||
|
||||
## Status
|
||||
|
||||
Proposed after some conversation between @lassulus, @Mic92, & @lopter.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# deployment parameters: evalHost, buildHost, targetHost
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
This section contains the architecture decisions that have been reviewed and generally agreed upon
|
||||
|
||||
## What is an ADR?
|
||||
@@ -11,6 +9,6 @@ This section contains the architecture decisions that have been reviewed and gen
|
||||
|
||||
## Crafting a new ADR
|
||||
|
||||
1. Use the [template](./_template.md)
|
||||
1. Use the [template](../decisions/template.md)
|
||||
2. Create the Pull request and gather feedback
|
||||
3. Retreive your adr-number (see: [numbering](./03-adr-numbering-process.md))
|
||||
3. Retreive your adr-number (see: [numbering](../decisions/03-adr-numbering-process.md))
|
||||
@@ -1,6 +1,6 @@
|
||||
# Decision record template by Michael Nygard
|
||||
## Decision record template by Michael Nygard
|
||||
|
||||
This is the template in [Documenting architecture decisions - Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
||||
This is the template in [Documenting architecture decisions - Michael Nygard](https://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
||||
You can use [adr-tools](https://github.com/npryce/adr-tools) for managing the ADR files.
|
||||
|
||||
In each ADR file, write these sections:
|
||||
@@ -1,12 +1,10 @@
|
||||
# How to add machines
|
||||
|
||||
Machines can be added using the following methods
|
||||
|
||||
- Create a file `machines/{machine_name}/configuration.nix` (See: [File Autoincludes](../inventory/autoincludes.md))
|
||||
- Create a file `machines/{machine_name}/configuration.nix` (See: [File Autoincludes](../guides/inventory/autoincludes.md))
|
||||
- Imperative via cli command: `clan machines create`
|
||||
- Editing nix expressions in flake.nix See [`clan-core.lib.clan`](/options/?scope=Flake Options (clan.nix file))
|
||||
- Editing nix expressions in flake.nix See [`clan-core.lib.clan`](../reference/options/clan.md)
|
||||
|
||||
See the complete [list](../inventory/autoincludes.md) of auto-loaded files.
|
||||
See the complete [list](../guides/inventory/autoincludes.md) of auto-loaded files.
|
||||
|
||||
## Create a machine
|
||||
|
||||
@@ -20,8 +18,6 @@ See the complete [list](../inventory/autoincludes.md) of auto-loaded files.
|
||||
};
|
||||
|
||||
# Additional NixOS configuration can be added here.
|
||||
# machines/jon/configuration.nix will be automatically imported.
|
||||
# See: https://docs.clan.lol/guides/more-machines/#automatic-registration
|
||||
machines = {
|
||||
# jon = { config, ... }: {
|
||||
# environment.systemPackages = [ pkgs.asciinema ];
|
||||
@@ -1,5 +1,3 @@
|
||||
# How to add services
|
||||
|
||||
A service in clan is a self-contained, reusable unit of system configuration that provides a specific piece of functionality across one or more machines.
|
||||
|
||||
Think of it as a recipe for running a tool — like automatic backups, VPN networking, monitoring, etc.
|
||||
@@ -10,7 +8,7 @@ In Clan Services are multi-Host & role-based:
|
||||
|
||||
- You can use tags instead of explicit machine names.
|
||||
|
||||
To learn more: [Guide about clanService](../inventory/clanServices.md)
|
||||
To learn more: [Guide about clanService](../guides/services/introduction-to-services.md)
|
||||
|
||||
!!! Important
|
||||
It is recommended to add at least one networking service such as `zerotier` that allows to reach all your clan machines from your setup computer across the globe.
|
||||
@@ -40,8 +38,8 @@ To learn more: [Guide about clanService](../inventory/clanServices.md)
|
||||
}
|
||||
```
|
||||
|
||||
1. See [reference/clanServices](../../reference/clanServices/index.md) for all available services and how to configure them.
|
||||
Or read [authoring/clanServices](../services/community.md) if you want to bring your own
|
||||
1. See [services/official](../services/definition.md) for all available services and how to configure them.
|
||||
Or read [guides/services](../guides/services/community.md) if you want to bring your own
|
||||
|
||||
2. Replace `__YOUR_CONTROLLER_` with the *name* of your machine.
|
||||
|
||||
@@ -73,5 +71,5 @@ Adding the following services is recommended for most users:
|
||||
```
|
||||
|
||||
1. The `admin` service will generate a **root-password** and **add your ssh-key** that allows for convienient administration.
|
||||
2. Equivalent to directly setting `authorizedKeys` like in [configuring a machine](./add-machines.md#configuring-a-machine)
|
||||
2. Equivalent to directly setting `authorizedKeys` like in [configuring a machine](../getting-started/add-machines.md#configuring-a-machine)
|
||||
3. Adds `user = jon` as a user on all machines. Will create a `home` directory, and prompt for a password before deployment.
|
||||
@@ -1,19 +1,17 @@
|
||||
# How to add users
|
||||
|
||||
!!! Note "Under construction"
|
||||
|
||||
The users concept of clan is not done yet. This guide outlines some solutions from our community.
|
||||
Defining users can be done in many different ways. We want to highlight two approaches:
|
||||
|
||||
- Using clan's [users](../../reference/clanServices/users.md) service.
|
||||
- Using clan's [users](../services/official/users.md) service.
|
||||
- Using a custom approach.
|
||||
|
||||
## Adding Users using the [users](../../reference/clanServices/users.md) service
|
||||
## Adding Users using the [users](../services/official/users.md) service
|
||||
|
||||
To add a first *user* this guide will be leveraging two things:
|
||||
|
||||
- [clanServices](../../reference/clanServices/index.md): Allows to bind arbitrary logic to something we call an `ìnstance`.
|
||||
- [clanServices/users](../../reference/clanServices/users.md): Implements logic for adding a single user perInstance.
|
||||
- [services](../services/definition.md): Allows to bind arbitrary logic to something we call an `ìnstance`.
|
||||
- [services/users](../services/official/users.md): Implements logic for adding a single user perInstance.
|
||||
|
||||
The example shows how to add a user called `jon`:
|
||||
|
||||
@@ -51,7 +49,7 @@ The example shows how to add a user called `jon`:
|
||||
|
||||
The `users` service creates a `/home/jon` directory, allows `jon` to sign in and will take care of the user's password.
|
||||
|
||||
For more information see [clanService/users](../../reference/clanServices/users.md)
|
||||
For more information see [services/users](../services/official/users.md)
|
||||
|
||||
## Using a custom approach
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Configure Disk Config
|
||||
|
||||
By default clan uses [disko](https://github.com/nix-community/disko) which allows for declarative disk partitioning.
|
||||
|
||||
To see what disk templates are available run:
|
||||
@@ -64,9 +62,9 @@ You can have a look and customize it if needed.
|
||||
|
||||
## Deploy the machine
|
||||
|
||||
**Finally deployment time!**
|
||||
**Finally deployment time!**
|
||||
|
||||
This command is destructive and will format your disk and install NixOS on it! It is equivalent to appending `--phases kexec,disko,install,reboot`.
|
||||
This command is destructive and will format your disk and install NixOS on it! It is equivalent to appending `--phases kexec,disko,install,reboot`.
|
||||
|
||||
|
||||
```bash
|
||||
@@ -1,10 +1,8 @@
|
||||
# Convert existing NixOS configurations
|
||||
|
||||
This guide will help you convert your existing NixOS configurations into a Clan.
|
||||
|
||||
!!! Warning
|
||||
Migrating instead of starting new can be trickier and might lead to bugs or
|
||||
unexpected issues. We recommend reading the [Getting Started](./index.md) guide first.
|
||||
unexpected issues. We recommend reading the [Getting Started](../getting-started/creating-your-first-clan.md) guide first.
|
||||
|
||||
Once you have a working setup and understand the concepts transfering your NixOS configurations over is easy.
|
||||
|
||||
@@ -171,7 +169,7 @@ Clan needs to know where it can reach your hosts. For testing purpose set
|
||||
}
|
||||
```
|
||||
|
||||
See our guide on for properly [configuring machines networking](../networking/networking.md)
|
||||
See our guide on for properly [configuring machines networking](../guides/networking/networking.md)
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# :material-clock-fast: Getting Started
|
||||
|
||||
Ready to manage your fleet of machines?
|
||||
Ready to manage your fleet of machines?
|
||||
|
||||
We will create a declarative infrastructure using **clan**, **git**, and **nix flakes**.
|
||||
|
||||
@@ -43,7 +41,7 @@ Make sure you have the following:
|
||||
## Create a New Clan
|
||||
|
||||
1. Navigate to your desired directory:
|
||||
|
||||
|
||||
```shellSession
|
||||
cd <your-directory>
|
||||
```
|
||||
@@ -76,7 +74,7 @@ my-clan/
|
||||
```
|
||||
|
||||
!!! note "Templates"
|
||||
This is the structure for the `default` template.
|
||||
This is the structure for the `default` template.
|
||||
|
||||
Use `clan templates list` and `clan templates --help` for available templates & more. Keep in mind that the exact files may change as templates evolve.
|
||||
|
||||
@@ -121,7 +119,7 @@ Name: __CHANGE_ME__
|
||||
Description: None
|
||||
```
|
||||
|
||||
This confirms your setup is working correctly.
|
||||
This confirms your setup is working correctly.
|
||||
|
||||
You can now change the default name by editing the `meta.name` field in your `clan.nix` file.
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
# USB Installer Image for Physical Machines
|
||||
|
||||
To install Clan on physical machines, you need to use our custom installer image. This is necessary for proper installation and operation.
|
||||
|
||||
!!! note "Deploying to a Virtual Machine?"
|
||||
If you're deploying to a virtual machine (VM), you can skip this section and go directly to the [Deploy Virtual Machine](./hardware-report-virtual.md) step. In this scenario, we automatically use [nixos-anywhere](https://github.com/nix-community/nixos-anywhere) to replace the kernel during runtime.
|
||||
If you're deploying to a virtual machine (VM), you can skip this section and go directly to the [Deploy Virtual Machine](../../getting-started/deploy-to-virtual-machine.md) step. In this scenario, we automatically use [nixos-anywhere](https://github.com/nix-community/nixos-anywhere) to replace the kernel during runtime.
|
||||
|
||||
??? info "Why nixos-anywhere Doesn't Work on Physical Hardware?"
|
||||
nixos-anywhere relies on [kexec](https://wiki.archlinux.org/title/Kexec) to replace the running kernel with our custom one. This method often has compatibility issues with real hardware, especially systems with dedicated graphics cards like laptops and servers, leading to crashes and black screens.
|
||||
@@ -152,7 +150,7 @@ sudo umount /dev/sdb1
|
||||
|
||||
## Boot From USB Stick
|
||||
|
||||
- To use, boot from the Clan USB drive with **secure boot turned off**. For step by step instructions go to [Disabling Secure Boot](../secure-boot.md)
|
||||
- To use, boot from the Clan USB drive with **secure boot turned off**. For step by step instructions go to [Disabling Secure Boot](../../guides/secure-boot.md)
|
||||
|
||||
## (Optional) Connect to Wifi Manually
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# Installing a Physical Machine
|
||||
|
||||
Now that you have created a machine, added some services, and set up secrets, this guide will walk you through how to deploy it.
|
||||
|
||||
|
||||
### Prerequisites
|
||||
- [x] RAM > 2GB
|
||||
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
|
||||
- [x] **Machine configuration**: See our basic [adding and configuring machine guide](./add-machines.md)
|
||||
- [x] **Initialized secrets**: See [secrets](../secrets.md) for how to initialize your secrets.
|
||||
- [x] **USB Flash Drive**: See [Clan Installer](./create-installer.md)
|
||||
- [x] **Machine configuration**: See our basic [adding and configuring machine guide](../../getting-started/add-machines.md)
|
||||
- [x] **Initialized secrets**: See [secrets](../../guides/secrets.md) for how to initialize your secrets.
|
||||
- [x] **USB Flash Drive**: See [Clan Installer](../../getting-started/deploy-to-physical-machine/flash-installer.md)
|
||||
|
||||
|
||||
### Image Installer
|
||||
This method makes use of the [image installers](./create-installer.md).
|
||||
This method makes use of the [image installers](../../getting-started/deploy-to-physical-machine/flash-installer.md).
|
||||
|
||||
The installer will randomly generate a password and local addresses on boot, then run a SSH server with these preconfigured.
|
||||
The installer shows its deployment relevant information in two formats, a text form, as well as a QR code.
|
||||
@@ -68,7 +66,7 @@ This is an example of the booted installer.
|
||||
```
|
||||
2. The root password for the installer medium.
|
||||
This password is autogenerated and meant to be easily typeable.
|
||||
3. See how to connect the installer medium to wlan [here](./create-installer.md).
|
||||
3. See how to connect the installer medium to wlan [here](../../getting-started/deploy-to-physical-machine/flash-installer.md).
|
||||
|
||||
!!!tip
|
||||
For easy sharing of deployment information via QR code, we highly recommend using [KDE Connect](https://apps.kde.org/de/kdeconnect/).
|
||||
@@ -113,4 +111,4 @@ The following command will generate a hardware report with [nixos-facter](https:
|
||||
|
||||
If you are using our template `[MACHINE]` would be `jon`
|
||||
|
||||
[Next Step (Choose Disk Format)](./choose-disk.md){ .md-button .md-button--primary }
|
||||
[Next Step (Choose Disk Format)](../../getting-started/configure-disk.md){ .md-button .md-button--primary }
|
||||
@@ -1,12 +1,8 @@
|
||||
# Generate a VM Hardware Report
|
||||
|
||||
Now that you have created a machine, added some services, and set up secrets, this guide will walk you through how to deploy it.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
- [x] RAM > 2GB
|
||||
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
|
||||
- [x] **Machine configuration**: See our basic [adding and configuring machine guide](./add-machines.md)
|
||||
- [x] **Machine configuration**: See our basic [adding and configuring machine guide](../getting-started/add-machines.md)
|
||||
|
||||
|
||||
Clan supports any cloud machine if it is reachable via SSH and supports `kexec`.
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# Update Machines
|
||||
|
||||
The Clan command line interface enables you to update machines remotely over SSH.
|
||||
@@ -32,7 +31,7 @@ Ensure that the root login is secured and only used when necessary.
|
||||
|
||||
## Multiple Target Hosts
|
||||
|
||||
You can now experiment with a new interface that allows you to define multiple `targetHost` addresses for different VPNs. Learn more and try it out in our [networking guide](../networking/networking.md).
|
||||
You can now experiment with a new interface that allows you to define multiple `targetHost` addresses for different VPNs. Learn more and try it out in our [networking guide](../guides/networking/networking.md).
|
||||
|
||||
## Updating Machine Configurations
|
||||
|
||||
@@ -79,7 +78,7 @@ clan {
|
||||
|
||||
`buildHost` / `targetHost`, and other network settings can be temporarily overridden for a single command:
|
||||
|
||||
For the full list of flags refer to the [Clan CLI](../../reference/cli/index.md)
|
||||
For the full list of flags refer to the [Clan CLI](../reference/cli/index.md)
|
||||
|
||||
```bash
|
||||
# Build on a remote host
|
||||
@@ -1,195 +0,0 @@
|
||||
This guide explains how to set up and manage
|
||||
[BorgBackup](https://borgbackup.readthedocs.io/) for secure, efficient backups
|
||||
in a clan network. BorgBackup provides:
|
||||
|
||||
- Space efficient storage of backups with deduplication
|
||||
- Secure, authenticated encryption
|
||||
- Compression: lz4, zstd, zlib, lzma or none
|
||||
- Mountable backups with FUSE
|
||||
- Easy installation on multiple platforms: Linux, macOS, BSD, …
|
||||
- Free software (BSD license).
|
||||
- Backed by a large and active open-source community.
|
||||
|
||||
## Borgbackup Example
|
||||
|
||||
```nix
|
||||
inventory.instances = {
|
||||
borgbackup = {
|
||||
module = {
|
||||
name = "borgbackup";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.client.machines."jon".settings = {
|
||||
destinations."storagebox" = {
|
||||
repo = "username@$hostname:/./borgbackup";
|
||||
rsh = ''ssh -oPort=23 -i /run/secrets/vars/borgbackup/borgbackup.ssh'';
|
||||
};
|
||||
};
|
||||
roles.server.machines = { };
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The input should be named according to your flake input. Jon is configured as a
|
||||
client machine with a destination pointing to a Hetzner Storage Box.
|
||||
|
||||
To see a list of all possible options go to [borgbackup clan service](../reference/clanServices/borgbackup.md)
|
||||
|
||||
## Roles
|
||||
|
||||
A Clan Service can have multiple roles, each role applies different nix config to the machine.
|
||||
|
||||
### 1. Client
|
||||
|
||||
Clients are machines that create and send backups to various destinations. Each
|
||||
client can have multiple backup destinations configured.
|
||||
|
||||
### 2. Server
|
||||
|
||||
Servers act as backup repositories, receiving and storing backups from client
|
||||
machines. They can be dedicated backup servers within your clan network.
|
||||
|
||||
## Backup destinations
|
||||
|
||||
This service allows you to perform backups to multiple `destinations`.
|
||||
Destinations can be:
|
||||
|
||||
- **Local**: Local disk storage
|
||||
- **Server**: Your own borgbackup server (using the `server` role)
|
||||
- **Third-party services**: Such as Hetzner's Storage Box
|
||||
|
||||
## State management
|
||||
|
||||
Backups are based on [states](../reference/clan.core/state.md). A state
|
||||
defines which files should be backed up and how these files are obtained through
|
||||
pre/post backup and restore scripts.
|
||||
|
||||
Here's an example for a user application `linkding`:
|
||||
|
||||
In this example:
|
||||
|
||||
- `/data/podman/linkding` is the application's data directory
|
||||
- `/var/backup/linkding` is the staging directory where data is copied for
|
||||
backup
|
||||
|
||||
```nix
|
||||
clan.core.state.linkding = {
|
||||
folders = [ "/var/backup/linkding" ];
|
||||
|
||||
preBackupScript = ''
|
||||
export PATH=${
|
||||
lib.makeBinPath [
|
||||
config.systemd.package
|
||||
pkgs.coreutils
|
||||
pkgs.rsync
|
||||
]
|
||||
}
|
||||
|
||||
service_status=$(systemctl is-active podman-linkding)
|
||||
|
||||
if [ "$service_status" = "active" ]; then
|
||||
systemctl stop podman-linkding
|
||||
rsync -avH --delete --numeric-ids "/data/podman/linkding/" /var/backup/linkding/
|
||||
systemctl start podman-linkding
|
||||
fi
|
||||
'';
|
||||
|
||||
postRestoreScript = ''
|
||||
export PATH=${
|
||||
lib.makeBinPath [
|
||||
config.systemd.package
|
||||
pkgs.coreutils
|
||||
pkgs.rsync
|
||||
]
|
||||
}
|
||||
|
||||
service_status="$(systemctl is-active podman-linkding)"
|
||||
|
||||
if [ "$service_status" = "active" ]; then
|
||||
systemctl stop podman-linkding
|
||||
|
||||
# Backup locally current linkding data
|
||||
cp -rp "/data/podman/linkding" "/data/podman/linkding.bak"
|
||||
|
||||
# Restore from borgbackup
|
||||
rsync -avH --delete --numeric-ids /var/backup/linkding/ "/data/podman/linkding/"
|
||||
|
||||
systemctl start podman-linkding
|
||||
fi
|
||||
'';
|
||||
};
|
||||
```
|
||||
|
||||
## Managing backups
|
||||
|
||||
In this section we go over how to manage your collection of backups with the clan command.
|
||||
|
||||
### Listing states
|
||||
|
||||
To see which files (`states`) will be backed up on a specific machine, use:
|
||||
|
||||
```bash
|
||||
clan state list jon
|
||||
```
|
||||
|
||||
This will show all configured states for the machine `jon`, for example:
|
||||
|
||||
```text
|
||||
· service: linkding
|
||||
folders:
|
||||
- /var/backup/linkding
|
||||
preBackupCommand: pre-backup-linkding
|
||||
postRestoreCommand: post-restore-linkding
|
||||
|
||||
· service: zerotier
|
||||
folders:
|
||||
- /var/lib/zerotier-one
|
||||
```
|
||||
|
||||
### Creating backups
|
||||
|
||||
To create a backup of a machine (e.g., `jon`), run:
|
||||
|
||||
```bash
|
||||
clan backups create jon
|
||||
```
|
||||
|
||||
This will backup all configured states (`zerotier` and `linkding` in this
|
||||
example) from the machine `jon`.
|
||||
|
||||
### Listing available backups
|
||||
|
||||
To see all available backups, use:
|
||||
|
||||
```bash
|
||||
clan backups list
|
||||
```
|
||||
|
||||
This will display all backups with their timestamps:
|
||||
|
||||
```text
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-jon-2025-07-22T19:40:10
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-jon-2025-07-23T01:00:00
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T01:00:00
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T06:02:35
|
||||
```
|
||||
|
||||
### Restoring backups
|
||||
|
||||
For restoring a backup you have two options.
|
||||
|
||||
#### Full restoration
|
||||
|
||||
To restore all services from a backup:
|
||||
|
||||
```bash
|
||||
clan backups restore jon borgbackup storagebox::u444061@u444061.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T06:02:35
|
||||
```
|
||||
|
||||
#### Partial restoration
|
||||
|
||||
To restore only a specific service (e.g., `linkding`):
|
||||
|
||||
```bash
|
||||
clan backups restore --service linkding jon borgbackup storagebox::u444061@u444061.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T06:02:35
|
||||
```
|
||||
71
docs/site/guides/backups/advanced-example.md
Normal file
71
docs/site/guides/backups/advanced-example.md
Normal file
@@ -0,0 +1,71 @@
|
||||
This guide explains how to set up a [Hetzner Storage Box](https://docs.hetzner.com/storage/storage-box/general) as a backup destination instead of using an internal Clan backup server. Follow the steps below to configure and verify the setup.
|
||||
|
||||
### Step 1: Create a Hetzner Storage Box
|
||||
|
||||
Begin by [creating a Hetzner Storage Box account](https://docs.hetzner.com/storage/storage-box/getting-started/creating-a-storage-box).
|
||||
|
||||
### Step 2: Create a Sub-Account
|
||||
|
||||
Set up a sub-account for your `jon` machine. Save the SSH password for this account in your password manager for future reference.
|
||||
|
||||
### Step 3: Configure BorgBackup in `clan.nix`
|
||||
|
||||
Add the BorgBackup service to your `clan.nix` configuration. In this example, the `jon` machine will back up to `user-sub1@user-sub1.your-storagebox.de` in the `borgbackup` folder:
|
||||
|
||||
```nix hl_lines="9"
|
||||
inventory.instances = {
|
||||
borgbackup = {
|
||||
module = {
|
||||
name = "borgbackup";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.client.machines."jon".settings = {
|
||||
destinations."storagebox" = {
|
||||
repo = "user-sub1@user-sub1.your-storagebox.de:/./borgbackup";
|
||||
rsh = ''ssh -p 23 -oStrictHostKeyChecking=accept-new -i /run/secrets/vars/borgbackup/borgbackup.ssh'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Step 4: Generate SSH Keys
|
||||
|
||||
Run the following command to generate the SSH private keys:
|
||||
|
||||
```bash
|
||||
clan vars generate
|
||||
```
|
||||
|
||||
### Step 5: Add the Public Key to the Sub-Account
|
||||
|
||||
Add the generated SSH public key to the `user-sub1` account by running:
|
||||
|
||||
```bash
|
||||
clan vars get jon borgbackup/borgbackup.ssh.pub | ssh -p23 user-sub1@user-sub1.your-storagebox.de install-ssh-key
|
||||
```
|
||||
|
||||
### Step 6: Deploy the Configuration
|
||||
|
||||
Apply the changes to your Clan setup by executing:
|
||||
|
||||
```bash
|
||||
clan machines update
|
||||
```
|
||||
|
||||
### Step 7: Verify the Setup
|
||||
|
||||
Check if the configuration works by starting the BorgBackup service on the `jon` machine:
|
||||
|
||||
```bash
|
||||
systemctl start borgbackup-job-storagebox.service &
|
||||
```
|
||||
|
||||
Then, inspect the service logs to ensure everything is functioning correctly:
|
||||
|
||||
```bash
|
||||
journalctl -u borgbackup-job-storagebox.service
|
||||
```
|
||||
|
||||
|
||||
|
||||
89
docs/site/guides/backups/backup-intro.md
Normal file
89
docs/site/guides/backups/backup-intro.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Introduction to Clan Backups
|
||||
|
||||
This guide explains how to use the Clan backup and state management interface to configure, manage, and restore backups for your services and machines. By the end of this guide, you will understand how to define backup states, manage backups, and restore data.
|
||||
|
||||
## State Management
|
||||
|
||||
Clan backups are based on the concept of [states](../../reference/clan.core/state.md). A state is a Nix attribute set, defined as `clan.core.state.<name> = {};`, which specifies the files or directories to back up.
|
||||
|
||||
For example, if you have a clan service called `linkding`, you can define the folders to back up as follows:
|
||||
|
||||
```nix hl_lines="2"
|
||||
clan.core.state.linkding = {
|
||||
folders = [ "/var/backup/linkding" ];
|
||||
};
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
- `/var/backup/linkding` is the staging directory where data is prepared for backup.
|
||||
|
||||
This simple configuration ensures that all critical data for the `linkding` service is included in the backup process.
|
||||
|
||||
|
||||
## Custom Pre and Post Backup Hooks
|
||||
|
||||
The state interface allows you to run custom scripts before creating a backup and after restoring one. These scripts are defined using the `preBackupScript` and `postRestoreScript` options. This can be useful for tasks like stopping services, syncing data, or performing cleanup operations.
|
||||
|
||||
### Example: Pre and Post Backup Scripts for the `linkding` Service
|
||||
|
||||
In the following example, we configure the `linkding` service to:
|
||||
|
||||
1. Stop the service before backing up its data.
|
||||
2. Sync the data to a staging directory.
|
||||
3. Restore the data and restart the service after restoration.
|
||||
|
||||
```nix hl_lines="5 26"
|
||||
clan.core.state.linkding = {
|
||||
folders = [ "/var/backup/linkding" ];
|
||||
|
||||
# Script to run before creating a backup
|
||||
preBackupScript = ''
|
||||
export PATH=${
|
||||
lib.makeBinPath [
|
||||
config.systemd.package
|
||||
pkgs.coreutils
|
||||
pkgs.rsync
|
||||
]
|
||||
}
|
||||
|
||||
# Check if the service is running
|
||||
service_status=$(systemctl is-active podman-linkding)
|
||||
|
||||
if [ "$service_status" = "active" ]; then
|
||||
# Stop the service and sync data to the backup directory
|
||||
systemctl stop podman-linkding
|
||||
rsync -avH --delete --numeric-ids "/data/podman/linkding/" /var/backup/linkding/
|
||||
systemctl start podman-linkding
|
||||
fi
|
||||
'';
|
||||
|
||||
# Script to run after restoring a backup
|
||||
postRestoreScript = ''
|
||||
export PATH=${
|
||||
lib.makeBinPath [
|
||||
config.systemd.package
|
||||
pkgs.coreutils
|
||||
pkgs.rsync
|
||||
]
|
||||
}
|
||||
|
||||
# Check if the service is running
|
||||
service_status="$(systemctl is-active podman-linkding)"
|
||||
|
||||
if [ "$service_status" = "active" ]; then
|
||||
# Stop the service
|
||||
systemctl stop podman-linkding
|
||||
|
||||
# Backup current data locally
|
||||
cp -rp "/data/podman/linkding" "/data/podman/linkding.bak"
|
||||
|
||||
# Restore data from the backup directory
|
||||
rsync -avH --delete --numeric-ids /var/backup/linkding/ "/data/podman/linkding/"
|
||||
|
||||
# Restart the service
|
||||
systemctl start podman-linkding
|
||||
fi
|
||||
'';
|
||||
};
|
||||
```
|
||||
75
docs/site/guides/backups/digging-deeper.md
Normal file
75
docs/site/guides/backups/digging-deeper.md
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
|
||||
In this section we go over how to manage your collection of backups with the clan command.
|
||||
|
||||
### Listing states
|
||||
|
||||
To see which files (`states`) will be backed up on a specific machine, use:
|
||||
|
||||
```bash
|
||||
clan state list jon
|
||||
```
|
||||
|
||||
This will show all configured states for the machine `jon`, for example:
|
||||
|
||||
```text
|
||||
· service: linkding
|
||||
folders:
|
||||
- /var/backup/linkding
|
||||
preBackupCommand: pre-backup-linkding
|
||||
postRestoreCommand: post-restore-linkding
|
||||
|
||||
· service: zerotier
|
||||
folders:
|
||||
- /var/lib/zerotier-one
|
||||
```
|
||||
|
||||
### Creating backups
|
||||
|
||||
To create a backup of a machine (e.g., `jon`), run:
|
||||
|
||||
```bash
|
||||
clan backups create jon
|
||||
```
|
||||
|
||||
This will backup all configured states (`zerotier` and `linkding` in this
|
||||
example) from the machine `jon`.
|
||||
|
||||
### Listing available backups
|
||||
|
||||
To see all available backups, use:
|
||||
|
||||
```bash
|
||||
clan backups list
|
||||
```
|
||||
|
||||
This will display all backups with their timestamps:
|
||||
|
||||
```text
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-jon-2025-07-22T19:40:10
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-jon-2025-07-23T01:00:00
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T01:00:00
|
||||
storagebox::username@username.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T06:02:35
|
||||
```
|
||||
|
||||
### Restoring backups
|
||||
|
||||
For restoring a backup you have two options.
|
||||
|
||||
#### Full restoration
|
||||
|
||||
To restore all services from a backup:
|
||||
|
||||
```bash
|
||||
clan backups restore jon borgbackup storagebox::u444061@u444061.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T06:02:35
|
||||
```
|
||||
|
||||
#### Partial restoration
|
||||
|
||||
To restore only a specific service (e.g., `linkding`):
|
||||
|
||||
```bash
|
||||
clan backups restore --service linkding jon borgbackup storagebox::u444061@u444061.your-storagebox.de:/./borgbackup::jon-storagebox-2025-07-24T06:02:35
|
||||
```
|
||||
|
||||
|
||||
63
docs/site/guides/backups/minimal-example.md
Normal file
63
docs/site/guides/backups/minimal-example.md
Normal file
@@ -0,0 +1,63 @@
|
||||
In this guide we will explain how to install a simple peer-to-peer backup system through the inventory. Such that machines will backup it's state to other machines in the clan, ensuring redundancy and data safety.
|
||||
|
||||
|
||||
### What is BorgBackup?
|
||||
|
||||
BorgBackup is a powerful and efficient backup solution designed for secure and space-efficient backups. It offers features such as:
|
||||
|
||||
- **Deduplication**: Saves storage space by avoiding duplicate data.
|
||||
- **Encryption**: Ensures backups are secure and authenticated.
|
||||
- **Compression**: Supports multiple compression algorithms like lz4, zstd, zlib, and more.
|
||||
- **FUSE Mounting**: Allows backups to be mounted as a file system.
|
||||
- **Cross-Platform**: Works on Linux, macOS, BSD, and more.
|
||||
- **Open Source**: Licensed under BSD and supported by an active community.
|
||||
|
||||
|
||||
While this guide uses BorgBackup, you can also use other backup services supported by Clan, depending on your requirements.
|
||||
|
||||
|
||||
### Example Setup
|
||||
|
||||
In this example, we configure a backup system with three machines: `bob`, `jon`, and `alice`. The `bob` and `jon` machines will periodically back up their state folders to `alice`. The backups are encrypted for security.
|
||||
|
||||
|
||||
|
||||
```nix
|
||||
inventory.instances = {
|
||||
borgbackup = {
|
||||
module = {
|
||||
name = "borgbackup";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.client.machines = {
|
||||
"bob" = { };
|
||||
"jon" = { };
|
||||
};
|
||||
roles.server.machines = {
|
||||
"alice" = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Roles
|
||||
|
||||
In a Clan Service, roles define how machines participate in the backup system. Each role applies specific Nix configurations to the machine, enabling flexibility and scalability in your backup setup.
|
||||
|
||||
- **Client**: These machines create backups and send them to designated destinations. Clients can be configured to back up to multiple destinations, ensuring redundancy and reliability.
|
||||
|
||||
- **Server**: These machines act as repositories, receiving and securely storing backups from client machines. Servers can be dedicated backup nodes within your clan network, providing centralized storage for all backups.
|
||||
|
||||
|
||||
## Backup destinations
|
||||
|
||||
This service allows you to perform backups to multiple `destinations`.
|
||||
Destinations can be:
|
||||
|
||||
- **Local**: Local disk storage
|
||||
- **Server**: Your own borgbackup server (using the `server` role)
|
||||
- **Third-party services**: Such as Hetzner's Storage Box
|
||||
|
||||
|
||||
However, if BorgBackup does not meet your needs, you can implement your own backup clan service.
|
||||
@@ -26,7 +26,7 @@ pkgs.mkShell {
|
||||
|
||||
## Debugging nixos-anywhere
|
||||
|
||||
If you encounter a bug in a complex shell script such as `nixos-anywhere`, start by replacing the `nixos-anywhere` command with a local checkout of the project, look in the [contribution](./CONTRIBUTING.md) section for an example.
|
||||
If you encounter a bug in a complex shell script such as `nixos-anywhere`, start by replacing the `nixos-anywhere` command with a local checkout of the project, look in the [contribution](../../guides/contributing/CONTRIBUTING.md) section for an example.
|
||||
|
||||
## The Debug Flag
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ This guide provides an example setup for a single-disk ZFS system with native en
|
||||
This configuration only applies to `systemd-boot` enabled systems and **requires** UEFI booting.
|
||||
|
||||
!!! Info "Secure Boot"
|
||||
This guide is compatible with systems that have [secure boot disabled](./secure-boot.md). If you encounter boot issues, check if secure boot needs to be disabled in your UEFI settings.
|
||||
This guide is compatible with systems that have [secure boot disabled](../guides/secure-boot.md). If you encounter boot issues, check if secure boot needs to be disabled in your UEFI settings.
|
||||
|
||||
Replace the highlighted lines with your own disk-id.
|
||||
You can find our your disk-id by executing:
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
Clan supports integration with [flake-parts](https://flake.parts/), a framework for constructing your `flake.nix` using modules. Follow these steps to integrate Clan with flake-parts:
|
||||
|
||||
Clan supports integration with [flake-parts](https://flake.parts/), a framework for constructing your `flake.nix` using modules.
|
||||
## Step 1: Update Your Flake Inputs
|
||||
|
||||
To construct your Clan using flake-parts, follow these steps:
|
||||
|
||||
## Update Your Flake Inputs
|
||||
|
||||
To begin, you'll need to add `flake-parts` as a new dependency in your flake's inputs. This is alongside the already existing dependencies, such as `clan-core` and `nixpkgs`. Here's how you can update your `flake.nix` file:
|
||||
Add `flake-parts` as a dependency in your `flake.nix` file alongside existing dependencies like `clan-core` and `nixpkgs`. Here's an example:
|
||||
|
||||
```nix
|
||||
# flake.nix
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
|
||||
# New flake-parts input
|
||||
# Add flake-parts
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
|
||||
clan-core = {
|
||||
url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
|
||||
inputs.nixpkgs.follows = "nixpkgs"; # Don't do this if your machines are on nixpkgs stable.
|
||||
# New
|
||||
inputs.flake-parts.follows = "flake-parts";
|
||||
inputs.nixpkgs.follows = "nixpkgs"; # Avoid this if using nixpkgs stable.
|
||||
inputs.flake-parts.follows = "flake-parts"; # New
|
||||
};
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Import the Clan flake-parts Module
|
||||
## Step 2: Import the Clan flake-parts Module
|
||||
|
||||
After updating your flake inputs, the next step is to import the Clan flake-parts module. This will make the [Clan options](/options) available within `mkFlake`.
|
||||
Next, import the Clan flake-parts module to make the [Clan options](../reference/options/clan.md) available within `mkFlake`:
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -43,9 +39,9 @@ After updating your flake inputs, the next step is to import the Clan flake-part
|
||||
}
|
||||
```
|
||||
|
||||
## Configure Clan Settings and Define Machines
|
||||
## Step 3: Configure Clan Settings and Define Machines
|
||||
|
||||
Next you'll need to configure Clan wide settings and define machines, here's an example of how `flake.nix` should look:
|
||||
Configure Clan-wide settings and define machines. Here's an example `flake.nix`:
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -62,24 +58,22 @@ Next you'll need to configure Clan wide settings and define machines, here's an
|
||||
];
|
||||
|
||||
# Define your Clan
|
||||
# See: https://docs.clan.lol/reference/nix-api/clan/
|
||||
clan = {
|
||||
# Clan wide settings
|
||||
meta.name = ""; # This is required and must be unique
|
||||
meta.name = ""; # Required and must be unique
|
||||
|
||||
machines = {
|
||||
jon = {
|
||||
imports = [
|
||||
./modules/firefox.nix
|
||||
# ... more modules
|
||||
# Add more modules as needed
|
||||
];
|
||||
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
|
||||
# Set this for Clan commands to work remotely over SSH like `clan machines update`
|
||||
# Enable remote Clan commands over SSH
|
||||
clan.core.networking.targetHost = "root@jon";
|
||||
|
||||
# remote> lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
||||
# Disk configuration
|
||||
disko.devices.disk.main = {
|
||||
device = "/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b448b4aec2929";
|
||||
};
|
||||
@@ -90,7 +84,4 @@ Next you'll need to configure Clan wide settings and define machines, here's an
|
||||
}
|
||||
```
|
||||
|
||||
For detailed information about configuring `flake-parts` and the available options within Clan,
|
||||
refer to the [Clan module](https://git.clan.lol/clan/clan-core/src/branch/main/flakeModules/clan.nix) documentation.
|
||||
|
||||
---
|
||||
For more details on configuring `flake-parts` and available Clan options, refer to the [Clan module documentation](https://git.clan.lol/clan/clan-core/src/branch/main/flakeModules/clan.nix).
|
||||
|
||||
@@ -17,13 +17,13 @@ The following tutorial will walk through setting up a Backup service where the t
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [x] [Add some machines](../getting-started/add-machines.md) to your Clan.
|
||||
- [x] [Add some machines](../../getting-started/add-machines.md) to your Clan.
|
||||
|
||||
## Services
|
||||
|
||||
The inventory defines `instances` of clan services. Membership of `machines` is defined via `roles` exclusively.
|
||||
|
||||
See each [modules documentation](../../reference/clanServices/index.md) for its available roles.
|
||||
See each [modules documentation](../../services/definition.md) for its available roles.
|
||||
|
||||
### Adding services to machines
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ This guide explains how to manage macOS machines using Clan.
|
||||
Currently, Clan supports the following features for macOS:
|
||||
|
||||
- `clan machines update` for existing [nix-darwin](https://github.com/nix-darwin/nix-darwin) installations
|
||||
- Support for [vars](./vars/vars-overview.md)
|
||||
- Support for [vars](../guides/vars/vars-overview.md)
|
||||
|
||||
## Add Your Machine to Your Clan Flake
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Migrating from using `clanModules` to `clanServices`
|
||||
|
||||
**Audience**: This is a guide for **people using `clanModules`**.
|
||||
If you are a **module author** and need to migrate your modules please consult our **new** [clanServices authoring guide](../services/community.md)
|
||||
If you are a **module author** and need to migrate your modules please consult our **new** [clanServices authoring guide](../../guides/services/community.md)
|
||||
|
||||
## What's Changing?
|
||||
|
||||
@@ -157,7 +157,7 @@ instances = {
|
||||
|
||||
### Move `services` entries to `instances`
|
||||
|
||||
Check if a service that you use has been migrated [In our reference](../../reference/clanServices/index.md)
|
||||
Check if a service that you use has been migrated [In our reference](../../services/definition.md)
|
||||
|
||||
In your inventory, move it from:
|
||||
|
||||
@@ -247,45 +247,45 @@ The following table shows the migration status of each deprecated clanModule:
|
||||
|
||||
| clanModule | Migration Status | Notes |
|
||||
|--------------------------|-------------------------------------------------------------------|------------------------------------------------------------------|
|
||||
| `admin` | ✅ [Migrated](../../reference/clanServices/admin.md) | |
|
||||
| `admin` | ✅ [Migrated](../../services/official/admin.md) | |
|
||||
| `auto-upgrade` | ❌ Removed | |
|
||||
| `borgbackup-static` | ❌ Removed | |
|
||||
| `borgbackup` | ✅ [Migrated](../../reference/clanServices/borgbackup.md) | |
|
||||
| `data-mesher` | ✅ [Migrated](../../reference/clanServices/data-mesher.md) | |
|
||||
| `borgbackup` | ✅ [Migrated](../../services/official/borgbackup.md) | |
|
||||
| `data-mesher` | ✅ [Migrated](../../services/official/data-mesher.md) | |
|
||||
| `deltachat` | ❌ Removed | |
|
||||
| `disk-id` | ❌ Removed | |
|
||||
| `dyndns` | ✅ [Migrated](../../reference/clanServices/dyndns.md) | |
|
||||
| `dyndns` | ✅ [Migrated](../../services/official/dyndns.md) | |
|
||||
| `ergochat` | ❌ Removed | |
|
||||
| `garage` | ✅ [Migrated](../../reference/clanServices/garage.md) | |
|
||||
| `garage` | ✅ [Migrated](../../services/official/garage.md) | |
|
||||
| `golem-provider` | ❌ Removed | |
|
||||
| `heisenbridge` | ❌ Removed | |
|
||||
| `importer` | ✅ [Migrated](../../reference/clanServices/importer.md) | |
|
||||
| `iwd` | ❌ Removed | Use [wifi service](../../reference/clanServices/wifi.md) instead |
|
||||
| `localbackup` | ✅ [Migrated](../../reference/clanServices/localbackup.md) | |
|
||||
| `importer` | ✅ [Migrated](../../services/official/importer.md) | |
|
||||
| `iwd` | ❌ Removed | Use [wifi service](../../services/official/wifi.md) instead |
|
||||
| `localbackup` | ✅ [Migrated](../../services/official/localbackup.md) | |
|
||||
| `localsend` | ❌ Removed | |
|
||||
| `machine-id` | ✅ [Migrated](../../reference/clan.core/settings.md) | Now an [option](../../reference/clan.core/settings.md) |
|
||||
| `matrix-synapse` | ✅ [Migrated](../../reference/clanServices/matrix-synapse.md) | |
|
||||
| `matrix-synapse` | ✅ [Migrated](../../services/official/matrix-synapse.md) | |
|
||||
| `moonlight` | ❌ Removed | |
|
||||
| `mumble` | ❌ Removed | |
|
||||
| `mycelium` | ✅ [Migrated](../../reference/clanServices/mycelium.md) | |
|
||||
| `mycelium` | ✅ [Migrated](../../services/official/mycelium.md) | |
|
||||
| `nginx` | ❌ Removed | |
|
||||
| `packages` | ✅ [Migrated](../../reference/clanServices/packages.md) | |
|
||||
| `packages` | ✅ [Migrated](../../services/official/packages.md) | |
|
||||
| `postgresql` | ✅ [Migrated](../../reference/clan.core/settings.md) | Now an [option](../../reference/clan.core/settings.md) |
|
||||
| `root-password` | ✅ [Migrated](../../reference/clanServices/users.md) | See [migration guide](../../reference/clanServices/users.md#migration-from-root-password-module) |
|
||||
| `root-password` | ✅ [Migrated](../../services/official/users.md) | See [migration guide](../../services/official/users.md#migration-from-root-password-module) |
|
||||
| `single-disk` | ❌ Removed | |
|
||||
| `sshd` | ✅ [Migrated](../../reference/clanServices/sshd.md) | |
|
||||
| `sshd` | ✅ [Migrated](../../services/official/sshd.md) | |
|
||||
| `state-version` | ✅ [Migrated](../../reference/clan.core/settings.md) | Now an [option](../../reference/clan.core/settings.md) |
|
||||
| `static-hosts` | ❌ Removed | |
|
||||
| `sunshine` | ❌ Removed | |
|
||||
| `syncthing-static-peers` | ❌ Removed | |
|
||||
| `syncthing` | ✅ [Migrated](../../reference/clanServices/syncthing.md) | |
|
||||
| `syncthing` | ✅ [Migrated](../../services/official/syncthing.md) | |
|
||||
| `thelounge` | ❌ Removed | |
|
||||
| `trusted-nix-caches` | ✅ [Migrated](../../reference/clanServices/trusted-nix-caches.md) | |
|
||||
| `user-password` | ✅ [Migrated](../../reference/clanServices/users.md) | |
|
||||
| `trusted-nix-caches` | ✅ [Migrated](../../services/official/trusted-nix-caches.md) | |
|
||||
| `user-password` | ✅ [Migrated](../../services/official/users.md) | |
|
||||
| `vaultwarden` | ❌ Removed | |
|
||||
| `xfce` | ❌ Removed | |
|
||||
| `zerotier-static-peers` | ❌ Removed | |
|
||||
| `zerotier` | ✅ [Migrated](../../reference/clanServices/zerotier.md) | |
|
||||
| `zerotier` | ✅ [Migrated](../../services/official/zerotier.md) | |
|
||||
| `zt-tcp-relay` | ❌ Removed | |
|
||||
|
||||
---
|
||||
@@ -378,6 +378,6 @@ instances = {
|
||||
|
||||
## Further reference
|
||||
|
||||
* [Inventory Concept](../inventory/inventory.md)
|
||||
* [Authoring a 'clan.service' module](../services/community.md)
|
||||
* [ClanServices](../inventory/clanServices.md)
|
||||
* [Inventory Concept](../../guides/inventory/inventory.md)
|
||||
* [Authoring a 'clan.service' module](../../guides/services/community.md)
|
||||
* [ClanServices](../../guides/services/introduction-to-services.md)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Migrate modules from `facts` to `vars`.
|
||||
# Migrate modules from `facts` to `vars`
|
||||
|
||||
For a high level overview about `vars` see our [blog post](https://clan.lol/blog/vars/).
|
||||
|
||||
This guide will help you migrate your modules that still use our [`facts`](../secrets.md) backend
|
||||
to the [`vars`](../vars/vars-overview.md) backend.
|
||||
This guide will help you migrate your modules that still use our [`facts`](../../guides/migrations/migration-facts-vars.md) backend
|
||||
to the [`vars`](../../guides/vars/vars-overview.md) backend.
|
||||
|
||||
The `vars` [module](../../reference/clan.core/vars.md) and the clan [command](../../reference/cli/vars.md) work in tandem, they should ideally be kept in sync.
|
||||
|
||||
@@ -33,7 +33,6 @@ vars.generators.vaultwarden = {
|
||||
|
||||
And this would read as follows: The vaultwarden `vars` module generates the admin file.
|
||||
|
||||
|
||||
## Prompts
|
||||
|
||||
Because prompts can be a necessity for certain systems `vars` have a shorthand for defining them.
|
||||
@@ -46,7 +45,9 @@ facts.services.forgejo-api = {
|
||||
generator.script = "cp $prompt_value > $secret/token";
|
||||
};
|
||||
```
|
||||
|
||||
To have analogous functionality in `vars`:
|
||||
|
||||
```nix
|
||||
vars.generators.forgejo-api = {
|
||||
prompts.token = {
|
||||
@@ -55,6 +56,7 @@ vars.generators.forgejo-api = {
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
This does not only simplify prompting, it also now allows us to define multiple prompts in one generator.
|
||||
A more analogous way to the `fact` method is available, in case the module author needs more flexibility with the prompt input:
|
||||
|
||||
@@ -92,8 +94,8 @@ facts.services.syncthing = {
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
This would be the corresponding `vars` module, which also will migrate existing facts.
|
||||
|
||||
```nix
|
||||
vars.generators.syncthing = {
|
||||
migrateFact = "syncthing";
|
||||
@@ -116,11 +118,11 @@ vars.generators.syncthing = {
|
||||
'';
|
||||
};
|
||||
```
|
||||
|
||||
Most of the usage patterns stay the same, but `vars` have a more ergonomic interface.
|
||||
There are not two different ways to define files anymore (public/secret).
|
||||
Now files are defined under the `files` attribute and are secret by default.
|
||||
|
||||
|
||||
## Happy Migration
|
||||
|
||||
We hope this gives you a clear path to start and finish your migration from `facts` to `vars`.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
This guide provides detailed instructions for configuring
|
||||
[ZeroTier VPN](https://zerotier.com) within Clan. Follow the
|
||||
outlined steps to set up a machine as a VPN controller (`<CONTROLLER>`) and to
|
||||
@@ -98,11 +97,12 @@ The status should be "ONLINE":
|
||||
```
|
||||
|
||||
## Further
|
||||
Currently **Zerotier** is the only mesh-vpn that is fully integrated into clan.
|
||||
In the future we plan to add additional network technologies like tinc, head/tailscale
|
||||
Currently we support yggdrassil and mycelium through usage of the inventory,
|
||||
though it is not yet integrated into the networking module.
|
||||
|
||||
Currently you can only use **Zerotier** as networking technology because this is the first network stack we aim to support.
|
||||
In the future we plan to add additional network technologies like tinc, head/tailscale, yggdrassil and mycelium.
|
||||
|
||||
We chose zerotier because in our tests it was a straight forwards solution to bootstrap.
|
||||
We chose ZeroTier because in our tests it was a straight forward solution to bootstrap.
|
||||
It allows you to selfhost a controller and the controller doesn't need to be globally reachable.
|
||||
Which made it a good fit for starting the project.
|
||||
|
||||
@@ -132,7 +132,7 @@ $ sudo zerotier-cli info
|
||||
|
||||
#### Manually Authorize a Machine on the Controller
|
||||
|
||||
=== "with ZerotierIP"
|
||||
=== "with ZeroTierIP"
|
||||
|
||||
```bash
|
||||
$ sudo zerotier-members allow --member-ip <IP>
|
||||
@@ -140,10 +140,10 @@ $ sudo zerotier-cli info
|
||||
|
||||
Substitute `<IP>` with the ZeroTier IP obtained previously.
|
||||
|
||||
=== "with ZerotierID"
|
||||
=== "with ZeroTierID"
|
||||
|
||||
```bash
|
||||
$ sudo zerotier-members allow <ID>
|
||||
```
|
||||
|
||||
Substitute `<ID>` with the ZeroTier ID obtained previously.
|
||||
Substitute `<ID>` with the ZeroTier ID obtained previously.
|
||||
|
||||
@@ -64,5 +64,5 @@ nixos-rebuild switch --flake .#my-machine --target-host root@target-ip --build-h
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Update Your Machines](getting-started/update.md) - Using clan's update command
|
||||
- [Variables (Vars)](vars/vars-overview.md) - Understanding the vars system
|
||||
- [Update Your Machines](../getting-started/update-machines.md) - Using clan's update command
|
||||
- [Variables (Vars)](../guides/vars/vars-overview.md) - Understanding the vars system
|
||||
|
||||
99
docs/site/guides/nixpkgs-flake-input/index.md
Normal file
99
docs/site/guides/nixpkgs-flake-input/index.md
Normal file
@@ -0,0 +1,99 @@
|
||||
**Q**: How should I choose the `nixpkgs` input for my flake when using `clan-core`?
|
||||
|
||||
**A**: Pin your flake to a recent `nixpkgs` version. Here are two common approaches, each with its trade-offs:
|
||||
|
||||
## Option 1: Follow `clan-core`
|
||||
|
||||
- **Pros**:
|
||||
- Recommended for most users.
|
||||
- Verified by our CI and widely used by others.
|
||||
- **Cons**:
|
||||
- Coupled to version bumps in `clan-core`.
|
||||
- Upstream features and packages may take longer to land.
|
||||
|
||||
Example:
|
||||
|
||||
```nix
|
||||
inputs = {
|
||||
clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
|
||||
# Use the `nixpkgs` version locked in `clan-core`
|
||||
nixpkgs.follows = "clan-core/nixpkgs";
|
||||
};
|
||||
```
|
||||
|
||||
## Option 2: Use Your Own `nixpkgs` Version
|
||||
|
||||
- **Pros**:
|
||||
- Faster access to new upstream features and packages.
|
||||
- **Cons**:
|
||||
- Recommended for advanced users.
|
||||
- Not covered by our CI — you’re on the frontier.
|
||||
|
||||
Example:
|
||||
|
||||
```nix
|
||||
inputs = {
|
||||
# Specify your own `nixpkgs` version
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
|
||||
# Ensure `clan-core` uses your `nixpkgs` version
|
||||
clan-core.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
```
|
||||
|
||||
## Recommended: Avoid Duplicate `nixpkgs` Entries
|
||||
|
||||
To prevent ambiguity or compatibility issues, check your `flake.lock` for duplicate `nixpkgs` entries. Duplicate entries indicate a missing `follows` directive in one of your flake inputs.
|
||||
|
||||
Example of duplicate entries in `flake.lock`:
|
||||
|
||||
```json
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 315532800,
|
||||
"narHash": "sha256-1tUpklZsKzMGI3gjo/dWD+hS8cf+5Jji8TF5Cfz7i3I=",
|
||||
"rev": "08b8f92ac6354983f5382124fef6006cade4a1c1",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre862603.08b8f92ac635/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1758346548,
|
||||
"narHash": "sha256-afXE7AJ7MY6wY1pg/Y6UPHNYPy5GtUKeBkrZZ/gC71E=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b2a3852bd078e68dd2b3dfa8c00c67af1f0a7d20",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To locate the source of duplicate entries, grep your `flake.lock` file. For example, if `home-manager` is referencing `nixpkgs_2` instead of the main `nixpkgs`:
|
||||
|
||||
```json
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Fix this by adding the following line to your `flake.nix` inputs:
|
||||
|
||||
```nix
|
||||
home-manager.inputs.nixpkgs.follows = "nixpkgs";
|
||||
```
|
||||
|
||||
Repeat this process until all duplicate `nixpkgs` entries are resolved. This ensures all inputs use the same `nixpkgs` source, preventing cross-version conflicts.
|
||||
@@ -1,5 +1,5 @@
|
||||
This article provides an overview over the underlying secrets system which is used by [Vars](./vars/vars-overview.md).
|
||||
Under most circumstances you should use [Vars](./vars/vars-overview.md) directly instead.
|
||||
This article provides an overview over the underlying secrets system which is used by [Vars](../guides/vars/vars-overview.md).
|
||||
Under most circumstances you should use [Vars](../guides/vars/vars-overview.md) directly instead.
|
||||
|
||||
Consider using `clan secrets` only for managing admin users and groups, as well as a debugging tool.
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
## Service Module Specification
|
||||
|
||||
This section explains how to author a clan service module.
|
||||
We discussed the initial architecture in [01-clan-service-modules](../../decisions/01-ClanModules.md) and decided to rework the format.
|
||||
We discussed the initial architecture in [01-clan-service-modules](../../decisions/01-Clan-Modules.md) and decided to rework the format.
|
||||
|
||||
For the full specification and current state see: **[Service Author Reference](../../reference/clanServices/clan-service-author-interface.md)**
|
||||
For the full specification and current state see: **[Service Author Reference](../../reference/options/clan_service.md)**
|
||||
|
||||
### A Minimal module
|
||||
|
||||
@@ -47,7 +47,7 @@ The imported module file must fulfill at least the following requirements:
|
||||
}
|
||||
```
|
||||
|
||||
For more attributes see: **[Service Author Reference](../../reference/clanServices/clan-service-author-interface.md)**
|
||||
For more attributes see: **[Service Author Reference](../../reference/options/clan_service.md)**
|
||||
|
||||
### Adding functionality to the module
|
||||
|
||||
@@ -300,6 +300,6 @@ instnaces.machine-type = {
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Reference Documentation for Service Authors](../../reference/clanServices/clan-service-author-interface.md)
|
||||
- [Migration Guide from ClanModules to ClanServices](../migrations/migrate-inventory-services.md)
|
||||
- [Decision that lead to ClanServices](../../decisions/01-ClanModules.md)
|
||||
- [Reference Documentation for Service Authors](../../reference/options/clan_service.md)
|
||||
- [Migration Guide from ClanModules to ClanServices](../../guides/migrations/migrate-inventory-services.md)
|
||||
- [Decision that lead to ClanServices](../../decisions/01-Clan-Modules.md)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Clan's inventory system is a composable way to define and deploy services across
|
||||
Clan's services are a modular way to define and deploy services across
|
||||
machines.
|
||||
|
||||
This guide shows how to **instantiate** a `clanService`, explains how service
|
||||
definitions are structured in your inventory, and how to pick or create services
|
||||
This guide shows how to **instantiate** a **service**, explains how service
|
||||
definitions are structured and how to pick or create services
|
||||
from modules exposed by flakes.
|
||||
|
||||
The term **Multi-host-modules** was introduced previously in the [nixus
|
||||
A similar term: **Multi-host-modules** was introduced previously in the [nixus
|
||||
repository](https://github.com/infinisil/nixus) and represents a similar
|
||||
concept.
|
||||
|
||||
@@ -68,7 +68,7 @@ inventory.instances = {
|
||||
## Module Settings
|
||||
|
||||
Each role might expose configurable options. See clan's [clanServices
|
||||
reference](../../reference/clanServices/index.md) for all available options.
|
||||
reference](../../services/definition.md) for all available options.
|
||||
|
||||
Settings can be set in per-machine or per-role. The latter is applied to all
|
||||
machines that are assigned to that role.
|
||||
@@ -155,13 +155,13 @@ inventory.instances = {
|
||||
|
||||
You can use services exposed by Clan's core module library, `clan-core`.
|
||||
|
||||
🔗 See: [List of Available Services in clan-core](../../reference/clanServices/index.md)
|
||||
🔗 See: [List of Available Services in clan-core](../../services/definition.md)
|
||||
|
||||
## Defining Your Own Service
|
||||
|
||||
You can also author your own `clanService` modules.
|
||||
|
||||
🔗 Learn how to write your own service: [Authoring a service](../services/community.md)
|
||||
🔗 Learn how to write your own service: [Authoring a service](../../guides/services/community.md)
|
||||
|
||||
You might expose your service module from your flake — this makes it easy for other people to also use your module in their clan.
|
||||
|
||||
@@ -177,7 +177,5 @@ ______________________________________________________________________
|
||||
|
||||
## What's Next?
|
||||
|
||||
- [Author your own clanService →](../services/community.md)
|
||||
- [Migrate from clanModules →](../migrations/migrate-inventory-services.md)
|
||||
|
||||
<!-- TODO: * [Understand the architecture →](../explanation/clan-architecture.md) -->
|
||||
- [Author your own clanService →](../../guides/services/community.md)
|
||||
- [Migrate from clanModules →](../../guides/migrations/migrate-inventory-services.md)
|
||||
@@ -140,5 +140,5 @@ clan machines update my-machine
|
||||
|
||||
## Migration from Facts
|
||||
|
||||
If you're currently using the legacy facts system, see our [Migration Guide](../migrations/migration-facts-vars.md) for step-by-step instructions on upgrading to vars.
|
||||
If you're currently using the legacy facts system, see our [Migration Guide](../../guides/migrations/migration-facts-vars.md) for step-by-step instructions on upgrading to vars.
|
||||
|
||||
|
||||
@@ -74,17 +74,18 @@ hide:
|
||||
<input type="checkbox" id="clan-readmore" class="clamp-toggle" />
|
||||
<div class="clamp-content">
|
||||
<p><a href="https://clan.lol/">Clan</a> is a peer-to-peer computer management framework that empowers you to selfhost in a reliable and scalable way</strong>.</p>
|
||||
<p>Built on NixOS, Clan provides a declarative interface for managing machines</strong> with automated <a href="./guides/secrets.md">secret management</a>, easy <a href="./guides/mesh-vpn.md">mesh VPN connectivity</a>, and <a href="./guides/backups.md">automated backups</a>.</p>
|
||||
<p>Built on NixOS, Clan provides a declarative interface for managing machines</strong> with <a href="/guides/vars/vars-overview/">Resource management</a>, <a href="/guides/networking/networking/">Networking</a>, and <a href="
|
||||
/guides/backups/backup-intro/">Backups</a>.</p>
|
||||
<p>Whether you're running a homelab or maintaining critical computing infrastructure, Clan will help reduce maintenance burden</strong> by allowing a git repository to define your whole network</strong> of computers.</p>
|
||||
<p>In combination with <a href="https://github.com/Mic92/sops-nix">sops-nix</a>, <a href="https://github.com/nix-community/nixos-anywhere">nixos-anywhere</a> and <a href="https://github.com/nix-community/disko">disko</a>, Clan makes it possible to have collaborative infrastructure</strong>.</p>
|
||||
<p>At the heart of Clan are <a href="./reference/clanServices/index.md">Clan Services</a> - the core concept that enables you to add functionality across multiple machines in your network. While Clan ships with essential core services, you can <a href="./guides/inventory/clanServices.md">create custom services</a> tailored to your specific needs.</p>
|
||||
<p>At the heart of Clan are <a href="/services/definition">Clan Services</a> - the core concept that enables you to add functionality across multiple machines in your network. While Clan ships with essential core services, everyone can <a href="/guides/services/community/">create custom services</a> tailored to your specific needs.</p>
|
||||
</div>
|
||||
<label class="clamp-more" for="clan-readmore"></label>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
[Get started](./guides/getting-started/index.md){ .md-button .md-button--primary }
|
||||
[Get started](./getting-started/creating-your-first-clan.md){ .md-button .md-button--primary }
|
||||
[View on Gitea](https://git.clan.lol/clan/clan-core){ .md-button }
|
||||
|
||||
## Guides
|
||||
@@ -127,7 +128,7 @@ hide:
|
||||
|
||||
Search all options
|
||||
|
||||
- [Services](./reference/clanServices/index.md)
|
||||
- [Services](./services/definition.md)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ This section of the site provides an overview of available options and commands
|
||||
---
|
||||
|
||||
- [Clan Configuration Option](/options) - for defining a Clan
|
||||
- Learn how to use the [Clan CLI](./cli/index.md)
|
||||
- Explore available [services](./clanServices/index.md)
|
||||
- [NixOS Configuration Options](./clan.core/index.md) - Additional options avilable on a NixOS machine.
|
||||
- Learn how to use the [Clan CLI](../reference/cli/index.md)
|
||||
- Explore available [services](../services/definition.md)
|
||||
- [NixOS Configuration Options](../reference/clan.core/index.md) - Additional options avilable on a NixOS machine.
|
||||
|
||||
---
|
||||
|
||||
|
||||
14
docs/site/services/definition.md
Normal file
14
docs/site/services/definition.md
Normal file
@@ -0,0 +1,14 @@
|
||||
**`clanServices`** are modular building blocks that simplify the configuration and orchestration of multi-host services.
|
||||
|
||||
Each `clanService`:
|
||||
|
||||
* Is a module of class **`clan.service`**
|
||||
* Can define **roles** (e.g., `client`, `server`)
|
||||
* Uses **`inventory.instances`** to configure where and how it is deployed
|
||||
|
||||
!!! Note
|
||||
`clanServices` are part of Clan's next-generation service model and are intended to replace `clanModules`.
|
||||
|
||||
See [Migration Guide](../guides/migrations/migrate-inventory-services.md) for help on migrating.
|
||||
|
||||
Learn how to use `clanServices` in practice in the [Using clanServices guide](../guides/services/introduction-to-services.md).
|
||||
32
flake.lock
generated
32
flake.lock
generated
@@ -13,11 +13,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758510393,
|
||||
"narHash": "sha256-Bd7gr2RBaDJn1Zppus1WpW6CCdtJFn9Ccv4x0/HqCjo=",
|
||||
"rev": "e8418ce6a514d3561ea7dd2242d4bb9b36bc003b",
|
||||
"lastModified": 1759140052,
|
||||
"narHash": "sha256-CpGdQRvgmBhEAlXNyrSfrDWcKoYYSGd+5Lw7mvlbt/A=",
|
||||
"rev": "8332273e734aa906e7a1b2fda80e631f2dc9d4c9",
|
||||
"type": "tarball",
|
||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/e8418ce6a514d3561ea7dd2242d4bb9b36bc003b.tar.gz"
|
||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/8332273e734aa906e7a1b2fda80e631f2dc9d4c9.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
@@ -51,11 +51,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756770412,
|
||||
"narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=",
|
||||
"lastModified": 1759362264,
|
||||
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "4524271976b625a4a605beefd893f270620fd751",
|
||||
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -71,11 +71,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758447883,
|
||||
"narHash": "sha256-yGA6MV0E4JSEXqLTb4ZZkmdJZcoQ8HUzihRRX12Bvpg=",
|
||||
"lastModified": 1758805352,
|
||||
"narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=",
|
||||
"owner": "nix-darwin",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "25381509d5c91bbf3c30e23abc6d8476d2143cd1",
|
||||
"rev": "c48e963a5558eb1c3827d59d21c5193622a1477c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -146,11 +146,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758425756,
|
||||
"narHash": "sha256-L3N8zV6wsViXiD8i3WFyrvjDdz76g3tXKEdZ4FkgQ+Y=",
|
||||
"lastModified": 1759635238,
|
||||
"narHash": "sha256-UvzKi02LMFP74csFfwLPAZ0mrE7k6EiYaKecplyX9Qk=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "e0fdaea3c31646e252a60b42d0ed8eafdb289762",
|
||||
"rev": "6e5a38e08a2c31ae687504196a230ae00ea95133",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -181,11 +181,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1758206697,
|
||||
"narHash": "sha256-/DbPkh6PZOgfueCbs3uzlk4ASU2nPPsiVWhpMCNkAd0=",
|
||||
"lastModified": 1758728421,
|
||||
"narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "128222dc911b8e2e18939537bed1762b7f3a04aa",
|
||||
"rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
90
flake.nix
90
flake.nix
@@ -56,55 +56,59 @@
|
||||
else
|
||||
(import ./devFlake/flake-compat.nix {
|
||||
src = ./devFlake;
|
||||
}).outputs;
|
||||
}).outputs.inputs;
|
||||
in
|
||||
flake-parts.lib.mkFlake { inherit inputs; } (
|
||||
{ ... }:
|
||||
flake-parts.lib.mkFlake
|
||||
{
|
||||
_module.args = {
|
||||
inherit inputs;
|
||||
specialArgs = {
|
||||
inherit privateInputs;
|
||||
};
|
||||
clan = {
|
||||
meta.name = "clan-core";
|
||||
inventory = {
|
||||
machines = {
|
||||
"test-darwin-machine" = {
|
||||
machineClass = "darwin";
|
||||
}
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
clan = {
|
||||
meta.name = "clan-core";
|
||||
inventory = {
|
||||
machines = {
|
||||
"test-darwin-machine" = {
|
||||
machineClass = "darwin";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
systems = import systems;
|
||||
imports = [
|
||||
flake-parts.flakeModules.modules
|
||||
]
|
||||
++
|
||||
# only importing existing paths allows to minimize the flake for test
|
||||
# by removing files
|
||||
filter pathExists [
|
||||
./checks/flake-module.nix
|
||||
./clanModules/flake-module.nix
|
||||
./clanServices/flake-module.nix
|
||||
./devShell.nix
|
||||
./docs/nix/flake-module.nix
|
||||
./flakeModules/demo_iso.nix
|
||||
./flakeModules/flake-module.nix
|
||||
./lib/filter-clan-core/flake-module.nix
|
||||
./lib/flake-module.nix
|
||||
./lib/flake-parts/clan-nixos-test.nix
|
||||
./nixosModules/clanCore/vars/flake-module.nix
|
||||
./nixosModules/flake-module.nix
|
||||
./pkgs/clan-cli/clan_cli/tests/flake-module.nix
|
||||
./pkgs/flake-module.nix
|
||||
./templates/flake-module.nix
|
||||
systems = import systems;
|
||||
imports = [
|
||||
flake-parts.flakeModules.modules
|
||||
]
|
||||
++ [
|
||||
(if pathExists ./flakeModules/clan.nix then import ./flakeModules/clan.nix inputs.self else { })
|
||||
]
|
||||
# Make treefmt-nix optional
|
||||
# This only works if you set inputs.clan-core.inputs.treefmt-nix.follows
|
||||
# to a non-empty input that doesn't export a flakeModule
|
||||
++ optional (pathExists ./formatter.nix && inputs.treefmt-nix ? flakeModule) ./formatter.nix;
|
||||
}
|
||||
);
|
||||
++
|
||||
# only importing existing paths allows to minimize the flake for test
|
||||
# by removing files
|
||||
filter pathExists [
|
||||
./checks/flake-module.nix
|
||||
./clanModules/flake-module.nix
|
||||
./clanServices/flake-module.nix
|
||||
./devShell.nix
|
||||
./docs/nix/flake-module.nix
|
||||
./flakeModules/demo_iso.nix
|
||||
./flakeModules/flake-module.nix
|
||||
./lib/filter-clan-core/flake-module.nix
|
||||
./lib/flake-module.nix
|
||||
./lib/flake-parts/clan-nixos-test.nix
|
||||
./nixosModules/clanCore/vars/flake-module.nix
|
||||
./nixosModules/flake-module.nix
|
||||
./pkgs/clan-cli/clan_cli/tests/flake-module.nix
|
||||
./pkgs/flake-module.nix
|
||||
./templates/flake-module.nix
|
||||
]
|
||||
++ [
|
||||
(if pathExists ./flakeModules/clan.nix then import ./flakeModules/clan.nix inputs.self else { })
|
||||
]
|
||||
# Make treefmt-nix optional
|
||||
# This only works if you set inputs.clan-core.inputs.treefmt-nix.follows
|
||||
# to a non-empty input that doesn't export a flakeModule
|
||||
++ optional (pathExists ./formatter.nix && inputs.treefmt-nix ? flakeModule) ./formatter.nix;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,26 +22,50 @@ in
|
||||
default = config.flake.clan.clanInternals;
|
||||
};
|
||||
# The clan module
|
||||
clan = lib.mkOption {
|
||||
description = "Clan module. Define your clan inside here";
|
||||
default = { };
|
||||
type = types.submoduleWith {
|
||||
class = "clan";
|
||||
specialArgs =
|
||||
# TODO: make these explizit options and deduplicate with lib.clan function
|
||||
let
|
||||
nixpkgs = inputs.nixpkgs or clan-core.inputs.nixpkgs;
|
||||
nix-darwin = inputs.nix-darwin or clan-core.inputs.nix-darwin;
|
||||
in
|
||||
{
|
||||
clan =
|
||||
# TODO: make these explizit options and deduplicate with lib.clan function
|
||||
let
|
||||
nixpkgs = inputs.nixpkgs or clan-core.inputs.nixpkgs;
|
||||
nix-darwin = inputs.nix-darwin or clan-core.inputs.nix-darwin;
|
||||
in
|
||||
lib.mkOption {
|
||||
description = "Clan module. Define your clan inside here";
|
||||
default = { };
|
||||
type = types.submoduleWith {
|
||||
class = "clan";
|
||||
specialArgs = {
|
||||
inherit self;
|
||||
inherit nixpkgs nix-darwin;
|
||||
};
|
||||
modules = [
|
||||
clan-core.modules.clan.default
|
||||
];
|
||||
modules = [
|
||||
clan-core.modules.clan.default
|
||||
{
|
||||
checks.minNixpkgsVersion = {
|
||||
assertion = lib.versionAtLeast nixpkgs.lib.version "25.11";
|
||||
message = ''
|
||||
Nixpkgs version: ${nixpkgs.lib.version} is incompatible with clan-core. (>= 25.11 is recommended)
|
||||
---
|
||||
Your version of 'nixpkgs' seems too old for clan-core.
|
||||
Please read: https://docs.clan.lol/guides/nixpkgs-flake-input
|
||||
|
||||
You can ignore this check by setting:
|
||||
clan.checks.minNixpkgsVersion.ignore = true;
|
||||
---
|
||||
'';
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
apply =
|
||||
config:
|
||||
lib.deepSeq (lib.mapAttrs (
|
||||
id: check:
|
||||
if check.ignore || check.assertion then
|
||||
null
|
||||
else
|
||||
throw "clan.checks.${id} failed with message\n${check.message}"
|
||||
) config.checks) config;
|
||||
};
|
||||
};
|
||||
|
||||
# Mapped flake toplevel outputs
|
||||
darwinConfigurations = lib.mkOption {
|
||||
|
||||
@@ -10,6 +10,71 @@ let
|
||||
]
|
||||
);
|
||||
|
||||
pushPositions = map (
|
||||
def:
|
||||
lib.mapAttrs (_n: v: {
|
||||
inherit (def) file;
|
||||
value = v;
|
||||
}) def.value
|
||||
);
|
||||
|
||||
unwrapNullOr =
|
||||
type:
|
||||
let
|
||||
typeName = type.name or null;
|
||||
in
|
||||
if typeName == "nullOr" then type.nestedTypes.name or null else typeName;
|
||||
|
||||
mergeAttrs =
|
||||
{ type, definitionsWithLocations }:
|
||||
let
|
||||
# Vendored merge from lib.types.attrsOf
|
||||
# Because we still cannot access highest prio for the individual attrs yet.
|
||||
elemType = type.nestedTypes.elemType;
|
||||
mergedAttrs = lib.zipAttrsWith (name: defs: lib.modules.mergeDefinitions ([ name ]) elemType defs) (
|
||||
pushPositions definitionsWithLocations
|
||||
);
|
||||
headType = unwrapNullOr elemType;
|
||||
nullable = elemType.name or null == "nullOr";
|
||||
total = elemType.name or null == "submodule";
|
||||
in
|
||||
lib.mapAttrs (_name: merged: {
|
||||
__this = {
|
||||
prio = merged.defsFinal'.highestPrio;
|
||||
files = map (def: def.file) merged.defsFinal'.values;
|
||||
inherit
|
||||
headType
|
||||
nullable
|
||||
total
|
||||
;
|
||||
};
|
||||
}) mergedAttrs;
|
||||
|
||||
/**
|
||||
Takes a set of options as returned by `configuration`
|
||||
|
||||
Returns a recursive structure that contains '__this' along with attribute names that map to the same structure.
|
||||
|
||||
Within the reserved attribute '__this' the following attributes are available:
|
||||
|
||||
- prio: The highest priority this option was defined with
|
||||
- files: A list of files this option was defined in
|
||||
- type: The type of this option (e.g. "string", "attrsOf
|
||||
- total: Whether this is a total object. Meaning all attributes are fixed. No additional attributes can be added. Or one of them removed.
|
||||
|
||||
Example Result:
|
||||
{
|
||||
foo = {
|
||||
__this = { ... };
|
||||
bar = {
|
||||
__this = { ... };
|
||||
};
|
||||
baz = {
|
||||
__this = { ... };
|
||||
};
|
||||
};
|
||||
}
|
||||
*/
|
||||
getPrios =
|
||||
{
|
||||
options,
|
||||
@@ -20,80 +85,99 @@ let
|
||||
lib.mapAttrs (
|
||||
_: opt:
|
||||
let
|
||||
prio = {
|
||||
__prio = opt.highestPrio;
|
||||
headType = unwrapNullOr opt.type;
|
||||
nullable = opt.type.name or null == "nullOr";
|
||||
total = opt.type.name or null == "submodule";
|
||||
|
||||
definitionInfo = {
|
||||
__this = {
|
||||
prio = opt.highestPrio or null;
|
||||
files = opt.files or [ ];
|
||||
inherit headType nullable total;
|
||||
};
|
||||
};
|
||||
filteredSubOptions = filterOptions (opt.type.getSubOptions opt.loc);
|
||||
|
||||
zipDefs = builtins.zipAttrsWith (_: vs: vs);
|
||||
# TODO: respect freeformType
|
||||
submodulePrios = getPrios {
|
||||
options =
|
||||
filterOptions
|
||||
opt.valueMeta.configuration.options or (throw "Please use a newer nixpkgs version >=25.11");
|
||||
};
|
||||
|
||||
prioPerValue =
|
||||
{ type, defs }:
|
||||
/**
|
||||
Maps attrsOf and lazyAttrsOf
|
||||
*/
|
||||
handleAttrsOf =
|
||||
type: defs: attrs:
|
||||
lib.mapAttrs (
|
||||
attrName: prioSet:
|
||||
let
|
||||
# Evaluate the submodule
|
||||
# Remove once: https://github.com/NixOS/nixpkgs/pull/391544 lands
|
||||
# This is currently a workaround to get the submodule options
|
||||
# It also has a certain loss of information, on nested attrsOf, which is rare, but not ideal.
|
||||
options = filteredSubOptions;
|
||||
modules = (
|
||||
[
|
||||
{
|
||||
inherit options;
|
||||
_file = "<artifical submodule>";
|
||||
}
|
||||
]
|
||||
++ map (config: { inherit config; }) defs.${attrName}
|
||||
);
|
||||
submoduleEval = lib.evalModules {
|
||||
inherit modules;
|
||||
};
|
||||
in
|
||||
(lib.optionalAttrs (prioSet ? highestPrio) {
|
||||
__prio = prioSet.highestPrio;
|
||||
})
|
||||
// (
|
||||
if type.nestedTypes.elemType.name == "submodule" then
|
||||
getPrios { options = submoduleEval.options; }
|
||||
else
|
||||
# Nested attrsOf
|
||||
(lib.optionalAttrs
|
||||
(type.nestedTypes.elemType.name == "attrsOf" || type.nestedTypes.elemType.name == "lazyAttrsOf")
|
||||
(
|
||||
prioPerValue {
|
||||
type = type.nestedTypes.elemType;
|
||||
defs = zipDefs defs.${attrName};
|
||||
} prioSet.value
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
name: meta:
|
||||
(mergeAttrs {
|
||||
inherit type;
|
||||
definitionsWithLocations = defs;
|
||||
}).${name}
|
||||
// handleMeta {
|
||||
inherit meta;
|
||||
definitionsWithLocations =
|
||||
(builtins.zipAttrsWith (_name: values: values) (pushPositions defs)).${name};
|
||||
}
|
||||
) attrs;
|
||||
|
||||
submodulePrios =
|
||||
/**
|
||||
Maps attrsOf and lazyAttrsOf
|
||||
*/
|
||||
handleListOf = list: { __list = lib.map (item: handleMeta { meta = item; }) list; };
|
||||
|
||||
/**
|
||||
Unwraps the valueMeta of an option based on its type
|
||||
*/
|
||||
handleMeta =
|
||||
{
|
||||
meta,
|
||||
definitionsWithLocations ? [ ],
|
||||
}:
|
||||
let
|
||||
modules = (opt.definitions ++ opt.type.getSubModules);
|
||||
submoduleEval = lib.evalModules {
|
||||
inherit modules;
|
||||
};
|
||||
hasType = meta ? _internal.type;
|
||||
type = meta._internal.type;
|
||||
in
|
||||
getPrios { options = filterOptions submoduleEval.options; };
|
||||
|
||||
if !hasType then
|
||||
{ }
|
||||
else if type.name == "submodule" then
|
||||
# TODO: handle types
|
||||
getPrios { options = filterOptions meta.configuration.options; }
|
||||
else if type.name == "attrsOf" || type.name == "lazyAttrsOf" then
|
||||
handleAttrsOf meta._internal.type definitionsWithLocations meta.attrs
|
||||
# TODO: Add index support in nixpkgs first
|
||||
# else if type.name == "listOf" then
|
||||
# handleListOf meta.list
|
||||
else
|
||||
throw "Yet Unsupported type: ${type.name}";
|
||||
in
|
||||
if opt ? type && opt.type.name == "submodule" then
|
||||
(prio) // submodulePrios
|
||||
(definitionInfo) // submodulePrios
|
||||
else if opt ? type && (opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf") then
|
||||
prio
|
||||
// (prioPerValue {
|
||||
type = opt.type;
|
||||
defs = zipDefs opt.definitions;
|
||||
} (lib.modules.mergeAttrDefinitionsWithPrio opt))
|
||||
definitionInfo // (handleAttrsOf opt.type opt.definitionsWithLocations opt.valueMeta.attrs)
|
||||
# TODO: Add index support in nixpkgs, otherwise we cannot
|
||||
else if opt ? type && (opt.type.name == "listOf") then
|
||||
definitionInfo // (handleListOf opt.valueMeta.list)
|
||||
else if opt ? type && opt._type == "option" then
|
||||
prio
|
||||
definitionInfo
|
||||
else
|
||||
getPrios { options = opt; }
|
||||
) filteredOptions;
|
||||
|
||||
getPriosLegacy = import ./getPriosLegacy.nix { inherit lib; };
|
||||
|
||||
wrap =
|
||||
{
|
||||
options,
|
||||
}:
|
||||
# Test _module.check for valueMeta
|
||||
# This option should always exist if options comes from a module evaluation
|
||||
if options._module.check ? valueMeta then
|
||||
getPrios { inherit options; }
|
||||
else
|
||||
getPriosLegacy { inherit options; };
|
||||
in
|
||||
{
|
||||
inherit getPrios;
|
||||
getPrios = wrap;
|
||||
}
|
||||
|
||||
98
lib/introspection/getPriosLegacy.nix
Normal file
98
lib/introspection/getPriosLegacy.nix
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
lib ? import <nixpkgs/lib>,
|
||||
}:
|
||||
let
|
||||
filterOptions = lib.filterAttrs (
|
||||
name: _:
|
||||
!builtins.elem name [
|
||||
"_module"
|
||||
"_freeformOptions"
|
||||
]
|
||||
);
|
||||
|
||||
# Use for nixpkgs < 25.11
|
||||
getPriosLegacy =
|
||||
{
|
||||
options,
|
||||
}:
|
||||
let
|
||||
filteredOptions = filterOptions options;
|
||||
in
|
||||
lib.mapAttrs (
|
||||
_: opt:
|
||||
let
|
||||
prio = {
|
||||
__prio = opt.highestPrio;
|
||||
};
|
||||
filteredSubOptions = filterOptions (opt.type.getSubOptions opt.loc);
|
||||
|
||||
zipDefs = builtins.zipAttrsWith (_: vs: vs);
|
||||
|
||||
prioPerValue =
|
||||
{ type, defs }:
|
||||
lib.mapAttrs (
|
||||
attrName: prioSet:
|
||||
let
|
||||
# Evaluate the submodule
|
||||
# Remove once: https://github.com/NixOS/nixpkgs/pull/391544 lands
|
||||
# This is currently a workaround to get the submodule options
|
||||
# It also has a certain loss of information, on nested attrsOf, which is rare, but not ideal.
|
||||
options = filteredSubOptions;
|
||||
modules = (
|
||||
[
|
||||
{
|
||||
inherit options;
|
||||
_file = "<artifical submodule>";
|
||||
}
|
||||
]
|
||||
++ map (config: { inherit config; }) defs.${attrName}
|
||||
);
|
||||
submoduleEval = lib.evalModules {
|
||||
inherit modules;
|
||||
};
|
||||
in
|
||||
(lib.optionalAttrs (prioSet ? highestPrio) {
|
||||
__prio = prioSet.highestPrio;
|
||||
})
|
||||
// (
|
||||
if type.nestedTypes.elemType.name == "submodule" then
|
||||
getPriosLegacy { options = submoduleEval.options; }
|
||||
else
|
||||
# Nested attrsOf
|
||||
(lib.optionalAttrs
|
||||
(type.nestedTypes.elemType.name == "attrsOf" || type.nestedTypes.elemType.name == "lazyAttrsOf")
|
||||
(
|
||||
prioPerValue {
|
||||
type = type.nestedTypes.elemType;
|
||||
defs = zipDefs defs.${attrName};
|
||||
} prioSet.value
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
submodulePrios =
|
||||
let
|
||||
modules = (opt.definitions ++ opt.type.getSubModules);
|
||||
submoduleEval = lib.evalModules {
|
||||
inherit modules;
|
||||
};
|
||||
in
|
||||
getPriosLegacy { options = filterOptions submoduleEval.options; };
|
||||
|
||||
in
|
||||
if opt ? type && opt.type.name == "submodule" then
|
||||
(prio) // submodulePrios
|
||||
else if opt ? type && (opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf") then
|
||||
prio
|
||||
// (prioPerValue {
|
||||
type = opt.type;
|
||||
defs = zipDefs opt.definitions;
|
||||
} (lib.modules.mergeAttrDefinitionsWithPrio opt))
|
||||
else if opt ? type && opt._type == "option" then
|
||||
prio
|
||||
else
|
||||
getPriosLegacy { options = opt; }
|
||||
) filteredOptions;
|
||||
in
|
||||
getPriosLegacy
|
||||
@@ -13,69 +13,119 @@ let
|
||||
};
|
||||
in
|
||||
evaledConfig;
|
||||
|
||||
# Return only used attributes, for test stability
|
||||
stableView =
|
||||
set:
|
||||
let
|
||||
mapProps =
|
||||
attrs:
|
||||
lib.intersectAttrs {
|
||||
files = null;
|
||||
prio = null;
|
||||
total = null;
|
||||
} attrs;
|
||||
in
|
||||
lib.mapAttrs (
|
||||
name: value:
|
||||
if name == "__this" then
|
||||
mapProps value
|
||||
else if name == "__list" then
|
||||
[ ]
|
||||
else if lib.isAttrs value then
|
||||
stableView value
|
||||
else
|
||||
value
|
||||
) set;
|
||||
|
||||
in
|
||||
{
|
||||
test_default = {
|
||||
expr = slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
{
|
||||
options.foo.bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = "Test Description";
|
||||
default = true;
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
};
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
{
|
||||
options.foo.bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
description = "Test Description";
|
||||
default = true;
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
}
|
||||
);
|
||||
expected = {
|
||||
foo.bar = {
|
||||
__prio = 1500;
|
||||
foo = {
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 1500;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
test_no_default = {
|
||||
expr = slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
{
|
||||
options.foo.bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
};
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
{
|
||||
options.foo.bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
}
|
||||
);
|
||||
expected = {
|
||||
foo.bar = {
|
||||
__prio = 9999;
|
||||
foo = {
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ ];
|
||||
prio = 9999;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test_submodule = {
|
||||
expr = slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
{
|
||||
options.foo = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
{
|
||||
options.foo = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
options = {
|
||||
bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
}
|
||||
);
|
||||
expected = {
|
||||
foo = {
|
||||
# Prio of the submodule itself
|
||||
__prio = 9999;
|
||||
|
||||
# Prio of the bar option within the submodule
|
||||
bar.__prio = 9999;
|
||||
__this = {
|
||||
files = [ ];
|
||||
prio = 9999;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ ];
|
||||
prio = 9999;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -87,6 +137,7 @@ in
|
||||
{
|
||||
options.foo = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
_file = "option";
|
||||
options = {
|
||||
normal = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
@@ -106,9 +157,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
_file = "default";
|
||||
foo.default = lib.mkDefault true;
|
||||
}
|
||||
{
|
||||
_file = "normal";
|
||||
foo.normal = false;
|
||||
}
|
||||
]
|
||||
@@ -116,16 +169,49 @@ in
|
||||
in
|
||||
{
|
||||
inherit evaluated;
|
||||
expr = slib.getPrios {
|
||||
options = evaluated.options;
|
||||
};
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options = evaluated.options;
|
||||
}
|
||||
);
|
||||
expected = {
|
||||
foo = {
|
||||
__prio = 100;
|
||||
normal.__prio = 100; # Set via other module
|
||||
default.__prio = 1000;
|
||||
optionDefault.__prio = 1500;
|
||||
unset.__prio = 9999;
|
||||
__this = {
|
||||
files = [
|
||||
"normal"
|
||||
"default"
|
||||
];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
default = {
|
||||
__this = {
|
||||
files = [ "default" ];
|
||||
prio = 1000;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
normal = {
|
||||
__this = {
|
||||
files = [ "normal" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
optionDefault = {
|
||||
__this = {
|
||||
files = [ "option" ];
|
||||
prio = 1500;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
unset = {
|
||||
__this = {
|
||||
files = [ ];
|
||||
prio = 9999;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -155,13 +241,25 @@ in
|
||||
in
|
||||
{
|
||||
inherit evaluated;
|
||||
expr = slib.getPrios {
|
||||
options = evaluated.options;
|
||||
};
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options = evaluated.options;
|
||||
}
|
||||
);
|
||||
expected = {
|
||||
foo = {
|
||||
__prio = 100;
|
||||
bar.__prio = 100; # Set via other module
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -216,27 +314,63 @@ in
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
{
|
||||
config.foo.nested = lib.mkForce {
|
||||
# <- 50 prio
|
||||
"bar" = 2;
|
||||
};
|
||||
}
|
||||
{
|
||||
config.foo = {
|
||||
"nested" = {
|
||||
"bar" = 2; # <- 100 prio ?
|
||||
};
|
||||
"other" = {
|
||||
"bar" = lib.mkForce 2; # <- 50 prio ?
|
||||
"other" = lib.mkForce {
|
||||
"bar" = 2; # <- 50 prio
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
in
|
||||
{
|
||||
expr = slib.getPrios { options = evaluated.options; };
|
||||
expr = stableView (slib.getPrios { options = evaluated.options; });
|
||||
expected = {
|
||||
foo.__prio = 100;
|
||||
|
||||
foo.nested.__prio = 100;
|
||||
foo.other.__prio = 100;
|
||||
|
||||
foo.nested.bar.__prio = 100;
|
||||
foo.other.bar.__prio = 50;
|
||||
foo = {
|
||||
__this = {
|
||||
files = [
|
||||
"<unknown-file>"
|
||||
"<unknown-file>"
|
||||
];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
nested = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 50;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
other = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 50;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
test_attrsOf_attrsOf_submodule =
|
||||
@@ -276,23 +410,293 @@ in
|
||||
in
|
||||
{
|
||||
inherit evaluated;
|
||||
expr = slib.getPrios { options = evaluated.options; };
|
||||
expr = stableView (slib.getPrios { options = evaluated.options; });
|
||||
expected = {
|
||||
foo.__prio = 100;
|
||||
foo = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
a = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
b = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
c = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
x = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
y = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
z = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
bar = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
test_attrsOf_submodule_default =
|
||||
let
|
||||
evaluated = eval [
|
||||
{
|
||||
options.machines = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
prim = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 2;
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.submodule { };
|
||||
default = { };
|
||||
};
|
||||
fludl = lib.mkOption {
|
||||
type = lib.types.submodule { };
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
({
|
||||
_file = "inventory.json";
|
||||
machines.jon = {
|
||||
prim = 3;
|
||||
};
|
||||
})
|
||||
({
|
||||
# _file = "clan.nix";
|
||||
machines.jon = { };
|
||||
})
|
||||
|
||||
# Sub A
|
||||
foo.a.__prio = 100;
|
||||
# a.b doesnt have a prio
|
||||
# a.c doesnt have a prio
|
||||
foo.a.b.bar.__prio = 100;
|
||||
foo.a.c.bar.__prio = 100;
|
||||
];
|
||||
in
|
||||
{
|
||||
inherit evaluated;
|
||||
expr = stableView (slib.getPrios { options = evaluated.options; });
|
||||
expected = {
|
||||
machines = {
|
||||
__this = {
|
||||
files = [
|
||||
"<unknown-file>"
|
||||
"inventory.json"
|
||||
];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
jon = {
|
||||
__this = {
|
||||
files = [
|
||||
"<unknown-file>"
|
||||
"inventory.json"
|
||||
];
|
||||
prio = 100;
|
||||
total = true;
|
||||
};
|
||||
fludl = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 1500;
|
||||
total = true;
|
||||
};
|
||||
};
|
||||
prim = {
|
||||
__this = {
|
||||
files = [ "inventory.json" ];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
settings = {
|
||||
__this = {
|
||||
files = [ "<unknown-file>" ];
|
||||
prio = 1500;
|
||||
total = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Sub X
|
||||
foo.x.__prio = 100;
|
||||
# x.y doesnt have a prio
|
||||
# x.z doesnt have a prio
|
||||
foo.x.y.bar.__prio = 100;
|
||||
foo.x.z.bar.__prio = 100;
|
||||
test_listof_submodule_list =
|
||||
let
|
||||
evaluated = eval [
|
||||
{
|
||||
options.list = lib.mkOption {
|
||||
type = lib.types.listOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
foo = lib.mkOption { };
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
({
|
||||
_file = "inventory.json";
|
||||
list = [
|
||||
# Incomplete entry, should not break introspection
|
||||
{ }
|
||||
{ }
|
||||
];
|
||||
})
|
||||
({
|
||||
_file = "clan.nix";
|
||||
list = [
|
||||
{ }
|
||||
{ }
|
||||
];
|
||||
})
|
||||
];
|
||||
in
|
||||
{
|
||||
inherit evaluated;
|
||||
expr = (slib.getPrios { options = evaluated.options; });
|
||||
expected = {
|
||||
list = {
|
||||
__list = lib.genList (_: {
|
||||
foo = {
|
||||
__this = {
|
||||
files = [ ];
|
||||
headType = "unspecified";
|
||||
nullable = false;
|
||||
prio = 9999;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
}) 4;
|
||||
|
||||
__this = {
|
||||
files = [
|
||||
"clan.nix"
|
||||
"inventory.json"
|
||||
];
|
||||
headType = "listOf";
|
||||
nullable = false;
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test_listOf_submodule_default =
|
||||
let
|
||||
evaluated = eval [
|
||||
{
|
||||
options.machines = lib.mkOption {
|
||||
type = lib.types.listOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
prim = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 2;
|
||||
};
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.submodule { };
|
||||
default = { };
|
||||
};
|
||||
fludl = lib.mkOption {
|
||||
type = lib.types.submodule { };
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
({
|
||||
_file = "inventory.json";
|
||||
machines = [
|
||||
{
|
||||
prim = 10;
|
||||
}
|
||||
];
|
||||
})
|
||||
({
|
||||
_file = "clan.nix";
|
||||
machines = [
|
||||
{
|
||||
prim = 3;
|
||||
}
|
||||
];
|
||||
})
|
||||
];
|
||||
in
|
||||
{
|
||||
inherit evaluated;
|
||||
expr = stableView (slib.getPrios { options = evaluated.options; });
|
||||
expected = {
|
||||
machines = {
|
||||
__list = [ ];
|
||||
__this = {
|
||||
files = [
|
||||
"clan.nix"
|
||||
"inventory.json"
|
||||
];
|
||||
prio = 100;
|
||||
total = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,36 @@
|
||||
}:
|
||||
let
|
||||
types = lib.types;
|
||||
|
||||
checkType = types.attrsOf (
|
||||
types.submodule {
|
||||
# Skip entire evaluation of this check
|
||||
options.ignore = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Ignores this check entirely";
|
||||
};
|
||||
|
||||
# Can only be defined once
|
||||
options.assertion = lib.mkOption {
|
||||
type = types.bool;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
The assertion that must hold true.
|
||||
|
||||
If false, the message is shown.
|
||||
'';
|
||||
};
|
||||
# Message shown when the assertion is false
|
||||
options.message = lib.mkOption {
|
||||
type = types.str;
|
||||
description = "Message shown when the assertion is false";
|
||||
};
|
||||
|
||||
# TODO: add severity levels?
|
||||
# Fail, Warn, Log
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
@@ -18,6 +48,17 @@ in
|
||||
visible = false;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
# id :: { assertion, message }
|
||||
checks = lib.mkOption {
|
||||
type = checkType;
|
||||
default = { };
|
||||
description = ''
|
||||
Assertions that must hold true when evaluating the clan.
|
||||
When the assertion fails, the message is shown and the evaluation is aborted.
|
||||
'';
|
||||
};
|
||||
|
||||
self = lib.mkOption {
|
||||
type = types.raw;
|
||||
default = self;
|
||||
@@ -237,7 +278,7 @@ in
|
||||
description = ''
|
||||
The `Inventory` submodule.
|
||||
|
||||
For details see the [Inventory](./inventory.md) documentation.
|
||||
For details see the [Inventory](/reference/options/clan_inventory.md) documentation.
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -140,6 +140,12 @@
|
||||
imports = [
|
||||
# Import the resolved module.
|
||||
# i.e. clan.modules.admin
|
||||
{
|
||||
options.module = lib.mkOption {
|
||||
type = lib.types.raw;
|
||||
default = (builtins.head instances).instance.module;
|
||||
};
|
||||
}
|
||||
(builtins.head instances).instance.resolvedModule
|
||||
] # Include all the instances that correlate to the resolved module
|
||||
++ (builtins.map (v: {
|
||||
|
||||
@@ -381,6 +381,13 @@ in
|
||||
roleName = name;
|
||||
in
|
||||
{
|
||||
options.description = mkOption {
|
||||
type = lib.types.nullOr types.str;
|
||||
description = "A short description of the role '${name}', explaining it's effect on the supplied machine.";
|
||||
example = "Connects the supplied machine as a '${name}' to the 'example' service.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
options.interface = mkOption {
|
||||
description = ''
|
||||
Abstract interface of the role.
|
||||
@@ -959,8 +966,21 @@ in
|
||||
(
|
||||
let
|
||||
failedAssertions = (lib.filterAttrs (_: v: !v.assertion) config.result.assertions);
|
||||
formatModule =
|
||||
if config.module.input != null then
|
||||
"${config.module.input}/${config.module.name}"
|
||||
else
|
||||
"<clan-core>/${config.module.name}";
|
||||
warningsWithNull = lib.mapAttrsToList (
|
||||
roleName: roleConfig:
|
||||
if (roleConfig.description == null) then
|
||||
"Missing description for role '${roleName}' of clanService '${formatModule}'"
|
||||
else
|
||||
null
|
||||
) config.roles;
|
||||
in
|
||||
{
|
||||
warnings = (lib.filter (v: v != null) warningsWithNull);
|
||||
assertions = lib.attrValues failedAssertions;
|
||||
}
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ let
|
||||
in
|
||||
{
|
||||
manifest = eval.config.manifest;
|
||||
roles = lib.mapAttrs (_n: _v: { }) eval.config.roles;
|
||||
roles = lib.mapAttrs (_n: v: { inherit (v) description; }) eval.config.roles;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import logging
|
||||
import threading
|
||||
import traceback
|
||||
import uuid
|
||||
from contextlib import ExitStack
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any, Protocol
|
||||
@@ -42,10 +44,14 @@ class ApiBridge(Protocol):
|
||||
from clan_app.middleware.base import MiddlewareContext # noqa: PLC0415
|
||||
|
||||
with ExitStack() as stack:
|
||||
# Capture the current call stack up to this point
|
||||
original_stack = traceback.format_stack()
|
||||
|
||||
context = MiddlewareContext(
|
||||
request=request,
|
||||
bridge=self,
|
||||
exit_stack=stack,
|
||||
original_traceback=original_stack,
|
||||
)
|
||||
|
||||
# Process through middleware chain
|
||||
@@ -55,11 +61,23 @@ class ApiBridge(Protocol):
|
||||
f"{middleware.__class__.__name__} => {request.method_name}",
|
||||
)
|
||||
middleware.process(context)
|
||||
except Exception as e: # noqa: BLE001
|
||||
except Exception as e:
|
||||
from clan_app.middleware.base import ( # noqa: PLC0415
|
||||
MiddlewareError,
|
||||
)
|
||||
|
||||
# If middleware fails, handle error
|
||||
log.exception(f"Middleware {middleware.__class__.__name__} failed")
|
||||
|
||||
error_msg = str(e)
|
||||
|
||||
if isinstance(e, MiddlewareError):
|
||||
# If it's already a MiddlewareError, use it directly
|
||||
error_msg = e.method_message
|
||||
|
||||
self.send_api_error_response(
|
||||
request.op_key or "unknown",
|
||||
str(e),
|
||||
error_msg,
|
||||
["middleware_error"],
|
||||
)
|
||||
return
|
||||
@@ -108,7 +126,13 @@ class ApiBridge(Protocol):
|
||||
timeout: Timeout in seconds when waiting for completion
|
||||
|
||||
"""
|
||||
op_key = request.op_key or "unknown"
|
||||
op_key = request.header.get("op_key", request.op_key)
|
||||
if not isinstance(op_key, str):
|
||||
msg = f"Expected op_key to be a string, got {type(op_key)}"
|
||||
raise TypeError(msg)
|
||||
|
||||
# Validate operation key
|
||||
self._validate_operation_key(op_key)
|
||||
|
||||
def thread_task(stop_event: threading.Event) -> None:
|
||||
set_should_cancel(lambda: stop_event.is_set())
|
||||
@@ -144,3 +168,15 @@ class ApiBridge(Protocol):
|
||||
"Request timeout",
|
||||
["api_bridge", request.method_name],
|
||||
)
|
||||
|
||||
def _validate_operation_key(self, op_key: str) -> None:
|
||||
"""Validate that the operation key is valid and not in use."""
|
||||
try:
|
||||
uuid.UUID(op_key)
|
||||
except ValueError as e:
|
||||
msg = f"op_key '{op_key}' is not a valid UUID"
|
||||
raise TypeError(msg) from e
|
||||
|
||||
if op_key in self.threads:
|
||||
msg = f"Operation key '{op_key}' is already in use. Please try again."
|
||||
raise ValueError(msg)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
"""Compatibility wrapper for relocated middleware components.
|
||||
|
||||
This module preserves the legacy import path ``clan_app.api.middleware`` while
|
||||
the actual middleware implementations now live in ``clan_app.middleware``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from warnings import warn
|
||||
|
||||
import clan_app.middleware as _middleware
|
||||
from clan_app.middleware import * # noqa: F403
|
||||
|
||||
warn(
|
||||
"clan_app.api.middleware is deprecated; use clan_app.middleware instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
__all__ = _middleware.__all__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user