Compare commits
232 Commits
check-the-
...
admin-migr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fa017bc7b | ||
|
|
c25910c796 | ||
|
|
0d417bf098 | ||
|
|
7609a9d0d7 | ||
|
|
9b1a4e8219 | ||
|
|
ef4b5cc9d5 | ||
|
|
bea10f7bc8 | ||
|
|
e8608ac830 | ||
|
|
2ecedb6535 | ||
|
|
96fb6c39f4 | ||
|
|
6e26d31ac6 | ||
|
|
77ec1e9e48 | ||
|
|
ea8b1aa34c | ||
|
|
2bd9141d2d | ||
|
|
f788313e97 | ||
|
|
89b70ffa6f | ||
|
|
ed1692574f | ||
|
|
1106c50924 | ||
|
|
e99e47da10 | ||
|
|
67def050fd | ||
|
|
c0d2787dee | ||
|
|
ecc327277c | ||
|
|
0064a8bfbc | ||
|
|
1e8b9def2a | ||
|
|
f0983ede5e | ||
|
|
10bc9e3e44 | ||
|
|
556fd8845e | ||
|
|
fab079af71 | ||
|
|
0370c1cf02 | ||
|
|
aa557f3a96 | ||
|
|
e8699e68b5 | ||
|
|
f8f31d430d | ||
|
|
3d345e0bca | ||
|
|
80711fcf72 | ||
|
|
35684090e3 | ||
|
|
8069b137f3 | ||
|
|
2fba6b15e8 | ||
|
|
cddee0ca86 | ||
|
|
0f3ab641d9 | ||
|
|
d5f90b2730 | ||
|
|
54335221d8 | ||
|
|
76b13476a5 | ||
|
|
bbed94d6de | ||
|
|
23a5c845b0 | ||
|
|
b933dcf2e2 | ||
|
|
8a755fff8c | ||
|
|
5726dd1010 | ||
|
|
b306c748b8 | ||
|
|
2682581c09 | ||
|
|
a0a5827157 | ||
|
|
8638861a87 | ||
|
|
c5a28e2655 | ||
|
|
0af36d0a4d | ||
|
|
34b63ca1d5 | ||
|
|
e24a6e23ad | ||
|
|
fd7ccaca1a | ||
|
|
4251d5ee0b | ||
|
|
0a8839bcc0 | ||
|
|
cb41aaafa1 | ||
|
|
9867b6a894 | ||
|
|
7459566c2b | ||
|
|
1c08d6dd25 | ||
|
|
14f4d65c47 | ||
|
|
43159cc2f0 | ||
|
|
9d8ebfd267 | ||
|
|
1e379f6fa7 | ||
|
|
b32a7749cf | ||
|
|
153da50d6f | ||
|
|
dd3bb314fd | ||
|
|
687f26eef1 | ||
|
|
afdb08643d | ||
|
|
0946d4316e | ||
|
|
462c0764b9 | ||
|
|
a748a27ddc | ||
|
|
baf686e83f | ||
|
|
03ddce83b7 | ||
|
|
45eb73680d | ||
|
|
7d39d49b30 | ||
|
|
698a39fafb | ||
|
|
b633db4f8e | ||
|
|
7b9d18f9eb | ||
|
|
51950329a3 | ||
|
|
16256440e6 | ||
|
|
dfbb860898 | ||
|
|
444fc3f820 | ||
|
|
572ce8885f | ||
|
|
0bee027251 | ||
|
|
334367c3f7 | ||
|
|
2371a5fa78 | ||
|
|
4792d8b1e3 | ||
|
|
ace0328a14 | ||
|
|
66c2d54961 | ||
|
|
e18efdd48f | ||
|
|
8b652866c7 | ||
|
|
7129c38675 | ||
|
|
caacf65dc0 | ||
|
|
f8723ab897 | ||
|
|
b877df4c6e | ||
|
|
bf04eabc21 | ||
|
|
5149ed9318 | ||
|
|
4d84180dd9 | ||
|
|
7571fdef74 | ||
|
|
7d55511d6f | ||
|
|
cbd7157cfc | ||
|
|
25faba4795 | ||
|
|
0f0bab7976 | ||
|
|
f0e18bbdfb | ||
|
|
6d4db71ea3 | ||
|
|
b21c98db7f | ||
|
|
df3fe00b8a | ||
|
|
7371085c05 | ||
|
|
6804327bca | ||
|
|
ec76d5f8e5 | ||
|
|
864cdf33a7 | ||
|
|
e6dbccd8e5 | ||
|
|
30ac51b313 | ||
|
|
00b12c2c51 | ||
|
|
085d726217 | ||
|
|
3e65a76dfe | ||
|
|
929632049e | ||
|
|
0d1e642dfd | ||
|
|
8af68cbd9d | ||
|
|
a44fee9eab | ||
|
|
b27f34aed3 | ||
|
|
cd23c9ff41 | ||
|
|
852a673839 | ||
|
|
763a403e9f | ||
|
|
78f8a080a8 | ||
|
|
af2a00bde3 | ||
|
|
47c44ec7ec | ||
|
|
1f66e90db1 | ||
|
|
89fbf723ca | ||
|
|
548fdfb877 | ||
|
|
e44d34ef99 | ||
|
|
acc6797c22 | ||
|
|
9fbd943f44 | ||
|
|
d42d9ad943 | ||
|
|
3c16ccdb38 | ||
|
|
7b9e431da7 | ||
|
|
05388c9c7e | ||
|
|
fe36aa4161 | ||
|
|
6829c7f2dd | ||
|
|
aa2e8eef88 | ||
|
|
9e07064ec5 | ||
|
|
305a361f56 | ||
|
|
db32e99082 | ||
|
|
50ac0266f5 | ||
|
|
b01691cb64 | ||
|
|
44b237d9be | ||
|
|
f8bbd91c4a | ||
|
|
401de330f8 | ||
|
|
51da7ed5e8 | ||
|
|
e170cc2641 | ||
|
|
8434f0fc35 | ||
|
|
d6bbb42dda | ||
|
|
6539a6a24f | ||
|
|
8a849eb90f | ||
|
|
15f691d5aa | ||
|
|
82949237b7 | ||
|
|
7abb8bb662 | ||
|
|
f4d34b1326 | ||
|
|
3b5c22ebcf | ||
|
|
a2ce48f8cc | ||
|
|
f6899166c7 | ||
|
|
f5277c989a | ||
|
|
03731a2a67 | ||
|
|
091a56f57d | ||
|
|
7351f7994c | ||
|
|
5770ea036c | ||
|
|
0d537a146e | ||
|
|
c430ff6253 | ||
|
|
f3f4ebfc71 | ||
|
|
b79446f97e | ||
|
|
6d75a5596e | ||
|
|
2d97119a3b | ||
|
|
d0ff114f6b | ||
|
|
20ab5a67c1 | ||
|
|
d445a353d5 | ||
|
|
b08a2bdb75 | ||
|
|
10fd3f6e43 | ||
|
|
e8c85e3237 | ||
|
|
6aa3ec66d8 | ||
|
|
b767a4a09c | ||
|
|
b0bd209638 | ||
|
|
b187d9b3d2 | ||
|
|
83d8c3d2f3 | ||
|
|
1ce482f8f7 | ||
|
|
8803b3e0b5 | ||
|
|
9b66af37eb | ||
|
|
9186961ccb | ||
|
|
ca594bbe95 | ||
|
|
5454076df7 | ||
|
|
f8e7292bc4 | ||
|
|
2ddb38a434 | ||
|
|
a99c832ed9 | ||
|
|
12882ed68d | ||
|
|
134c545782 | ||
|
|
7889192b7c | ||
|
|
05a18baecb | ||
|
|
e6ebca8588 | ||
|
|
fcf1c683c5 | ||
|
|
db215a48b5 | ||
|
|
1df62bd2f2 | ||
|
|
ea1c8b9503 | ||
|
|
511b107511 | ||
|
|
47bcec69ab | ||
|
|
47203d849e | ||
|
|
7b4b700c33 | ||
|
|
69d394088b | ||
|
|
4c1e346cf2 | ||
|
|
be9a43c50b | ||
|
|
049d41f35c | ||
|
|
055bd1edd5 | ||
|
|
9ae44db29c | ||
|
|
17a6eda4b1 | ||
|
|
6beba157fe | ||
|
|
a14dcf4adb | ||
|
|
9bc23690a3 | ||
|
|
5b0334adda | ||
|
|
45639c0d4f | ||
|
|
dfa861428f | ||
|
|
f15cd773c5 | ||
|
|
1a24a05034 | ||
|
|
e07551cecf | ||
|
|
1f4b526e42 | ||
|
|
8a4fe1405a | ||
|
|
f7e0345ab3 | ||
|
|
11afc1faef | ||
|
|
c0964e1b22 | ||
|
|
f8c5b178a4 | ||
|
|
93090b74e5 | ||
|
|
839f8fb347 |
29
.gitea/workflows/update-clan-core-for-checks.yml
Normal file
29
.gitea/workflows/update-clan-core-for-checks.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: "Update pinned clan-core for checks"
|
||||
on:
|
||||
repository_dispatch:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "51 2 * * *"
|
||||
jobs:
|
||||
update-pinned-clan-core:
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Update clan-core for checks
|
||||
run: nix run .#update-clan-core-for-checks
|
||||
- name: Create pull request
|
||||
run: |
|
||||
git commit -am ""
|
||||
git push origin HEAD:update-clan-core-for-checks
|
||||
curl -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"head": "update-clan-core-branch",
|
||||
"base": "main",
|
||||
"title": "Automated Update: Clan Core",
|
||||
"body": "This PR updates the pinned clan-core for checks."
|
||||
}' \
|
||||
"${GITEA_SERVER_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/pulls"
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -14,8 +14,12 @@ example_clan
|
||||
nixos.qcow2
|
||||
**/*.glade~
|
||||
/docs/out
|
||||
/pkgs/clan-cli/clan_cli/select
|
||||
**/.local.env
|
||||
|
||||
# MacOS stuff
|
||||
**/.DS_store
|
||||
|
||||
# dream2nix
|
||||
.dream2nix
|
||||
|
||||
@@ -39,3 +43,6 @@ repo
|
||||
node_modules
|
||||
dist
|
||||
.webui
|
||||
|
||||
# TODO: remove after bug in select is fixed
|
||||
select
|
||||
|
||||
2
CODEOWNERS
Normal file
2
CODEOWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
nixosModules/clanCore/vars/.* @lopter
|
||||
pkgs/clan-cli/clan_cli/(secrets|vars)/.* @lopter
|
||||
64
checks/admin/default.nix
Normal file
64
checks/admin/default.nix
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
pkgs,
|
||||
self,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6zj7ubTg6z/aDwRNwvM/WlQdUocMprQ8E92NWxl6t+ test@test";
|
||||
in
|
||||
|
||||
clanLib.test.makeTestClan {
|
||||
inherit pkgs self;
|
||||
nixosTest = (
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
name = "admin";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
modules."@clan/admin" = ../../clanServices/admin/default.nix;
|
||||
inventory = {
|
||||
|
||||
machines.client = { };
|
||||
machines.server = { };
|
||||
|
||||
instances = {
|
||||
ssh-test-one = {
|
||||
module.name = "@clan/admin";
|
||||
roles.default.machines."server".settings = {
|
||||
allowedKeys.testkey = public-key;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nodes = {
|
||||
client.environment.etc.private-test-key.source = ./private-test-key;
|
||||
|
||||
server = {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.UsePAM = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
machines = [client, server]
|
||||
for m in machines:
|
||||
m.systemctl("start network-online.target")
|
||||
|
||||
for m in machines:
|
||||
m.wait_for_unit("network-online.target")
|
||||
|
||||
client.succeed(f"ssh -F /dev/null -i /etc/private-test-key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes root@server true &>/dev/null")
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
8
checks/admin/private-test-key
Normal file
8
checks/admin/private-test-key
Normal file
@@ -0,0 +1,8 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfgAAAJDXdRkm13UZ
|
||||
JgAAAAtzc2gtZWQyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfg
|
||||
AAAECIgb2FQcgBKMniA+6zm2cwGre60ATu3Sg1GivgAqVJlI6zj7ubTg6z/aDwRNwvM/Wl
|
||||
QdUocMprQ8E92NWxl6t+AAAAC3BpbnBveEBraXdpAQI=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
|
||||
6
checks/admin/sops/machines/server/key.json
Executable file
6
checks/admin/sops/machines/server/key.json
Executable file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
15
checks/admin/sops/secrets/server-age.key/secret
Normal file
15
checks/admin/sops/secrets/server-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:ET/FggP6t7L60krfVRvtMjv++xr3zqRsJ58AfnPS1zjTovV5tE9RgnboGY1ieS7fCs4VOL2S6ELtwV1+BTLDQX9s0c5A9cKqjnc=,iv:6EQ6DOqxUdHcOziTxf8kl0sp1Pggu720s5BJ8zA9Je0=,tag:hQMPWaWb4igqDYjwNehlqQ==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRWjhuZkgwNEZTL3JXZHFE\nTC9jSXJGcVd2bnkvOE1qV0d6TzNobFZobndvCmF1UmhVUWtKeVVwS29NY21ONkRn\nZU5sM01kTU9rQVNENi9paUFWbERoWnMKLS0tIEdjZzgwQjFtWlVtRGZwdW9GY0FK\nSER1TTFNVGxFa0ZrclR4MitWVERiSGMK9DNLzlJZelcpP0klwSDMggTAy5ZVOmsZ\niuu8dXMSdIeTd7l8rpZZN27BaKUm8yEDpUmot5Vq9rbZl6SO3ncX+A==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:41Z",
|
||||
"mac": "ENC[AES256_GCM,data:m8eTnPtMzrooEah43mvjwHxQIwR/aq+A1wYyG/rQ75COq/TQepfMiDSrCJKW8x+OKmN/3HZs1b9k659jNNMF+RtMag0+/ovTmr7PQux3IkzWl+R2kU3Y7WDOMweBKY3mTMu6reICE1YVME8vJwhDDbA5JCXJv64rkTz2tfGt4CQ=,iv:/vrwJyEVsfm1cUK//TesY24Makt8YI8mwx5GIhn4038=,tag:H2tS9ohvWJ4TWB6LghcZNg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
1
checks/admin/sops/secrets/server-age.key/users/admin
Symbolic link
1
checks/admin/sops/secrets/server-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../users/admin
|
||||
4
checks/admin/sops/users/admin/key.json
Normal file
4
checks/admin/sops/users/admin/key.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVVQjCEuryZii1LmJyjx9DX44eJh3qwTTEWlahYONsz nixbld@kiwi
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:yH7IQixe4nudnK4QOsr7VYoJ1YrVLP0Ufvgu7TNWSJnc55khKZHvQiDIlxzCIrAyMgUYwPNmrrZn9PZhgjAQZm7/o6SmP91Efb0yWM55o861El6v59yw0fseo3z6xAisjlg3KwTd5KMrRhzT0HzrjLn89SYRVh7DAWK+Cs7HVGvKVJ1E6AWiJmFPXIB7YaqJ7P4jZW9u7bEMCZabsRRqgS8dWXVXw9VS5ll4bNYQY4x5p2eg6e81zdeY2Y9Gbi5ty1Whqpzko2Pvggu6K4zUDXikM4lWggvIXzfrJA7HNE3xzXw94J45woj1y5FVOzn1Ve5kCc8PjVGaJ32poGkZiiD07kd5PxZuyVexREJpgz29lyB6nRJJeau4gpSG1VHOyNdwwBsBBm+zn6v2rlVzJPTlqmCV1+5UKf8JZKziIDFfi/78kSdtaeX+miJJvyDRkqNpQ7htEI0TAS8yQrkjWEIyaPAWQ2Usa8g1UrEftTlGUi/aMC2ob0qTLQQbhNhlSV/dImzI/qRMqSy2RWeS,iv:EuprKOFKzNLZrGlPtU2mEjmtNPNOcuVDbuvrtYyrerc=,tag:ny/q1AMHIQ8OgUNEE0Cc8w==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLODFxUjREa2tOYW9xaHYw\nQlhWZ282UVhiOGRndk0xYnlCQWRYR01qS2hJCllySUZyblJmTkgyZXd5bjVINDBo\nbEhIWmxycVdOVW0xTUxkalF5Y1k2bXcKLS0tIGRRS1VqOG5sanh2dXR5a2FGeXRs\nK3ZUdERCdEkvMmt3ZndPZEM3QUxJZzAKutOr9jHPCL86zEdMWJ6YZmplcr4tDAcN\nncQfC5rddYDW+0y/crwepKTa2FZjQheOY7jobZanU19ai521hqDSVw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxc3NxNGhRYmU3eFNodDZ4\ndnNTeHFnNXBKbUxmNHBjRlFpNG0zdVNpS2d3CjhrOUlSQU5BZVlSdWR3dnNyODZO\nRFBKZWpwWHlOUW03OGlVZlRQUmMrMzQKLS0tIEd6ei9LU3ZFTzlWTUk1c3huS1RQ\nbG1vQzI4ODJkeFcyRnJaQWp1Wk9zSkUKXefMOk/ZT4P6DItfnM82RoOvX4SBn7Fn\nlAoMnSzaRCunDwq7ha05G45gcI2Wjv3urjt0tmdmrmTnFtBSSt23TQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:47Z",
|
||||
"mac": "ENC[AES256_GCM,data:ORCANHbEX13O+zBVLOYyPxYIr1RS3NybTBb23ES7RbiGhSl2t/TXcfPWU5Smuqee0tfcrxL0u1FELZta4IysySW54JlD2907E9OUJWlQ6seOxADla4TMukW2pwhSsUJ9XfjEwC07zYB0alHzO3pY+LG3OAWzyhAlWzHlB5+WqIA=,iv:As+CjAJxKht0PJs3S2WWzho7UBqaUUltBIrYvlzBAbM=,tag:PSyUKaPZZNCxqd6XLPJSCw==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:5Fa0TQN/Whj311JZuVWXnp+2KJaNZPb/TOnP23T+KktulabcBA9go+/F+8wJbsEH2mf6UDq656p6C+kLIvfBFl2O/WwSOhsl23as9TLbgB6gBq73GjyV81VFsnLYNLHKMq+8nfJHM/WekA==,iv:n5vz3q5N6DplLWibdiCcYDdiN7q1VggzPoIYy9r2ZJw=,tag:FoGXrrJfjHZCUVTS2RESmw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBheXZvUW9YbjBFMi9mZnVk\ncGFPQzFOZkNPMU1HckhtSGtDWExpWVNYRlV3CjdDaDlSd2wzVnhKZGU0aFY0UnZY\nQStPSkxuSmlyOU9aeUdRaEJ2UTRRSm8KLS0tIFd3SG9YdEU5T2tzNk16b2s1SUNj\nWkh2cng5eWd3ZmxVZDhSR2Y1QnFySDgKGb/t+8NqiSGgmFOJc1NmDYZ+PXlANy8V\nuFwUTeqWAv7pOiGC8oessfyTPaJ7gWjz+XfKV5JVVikK2l3J4eAGxg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWM0daWmxCTjAyQStwQ2lM\nNkcyZW9hRmpDelRJR0VVTWhNTGFuZWhCc1RJCm81ZXowZjBhWGpIQTBhQnZLSmQy\nVUNNYjI0bVpqQ21YZS95TW53OUx1YUkKLS0tIDRUUE1zczBDeFJTOTQyVXVkMkYy\ncVVTN3J6TWtwcXVpM0M5c0gxUXpmV2cKwlWrbGLtkO2+PXKoMoHTV5aJpnfVy3RP\n6i8DDpLPGYfVUtWxHx+L+NmMxmw1AvmKSbdB4Y7aSbBW2mea3j1YCg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:50Z",
|
||||
"mac": "ENC[AES256_GCM,data:rwdbGOg8l8fWT2GYFx+PgV3oPxt5+NCHJf3PhG3V2lrRMPRisyf1nKwDsYavTuhv+bZC/qo4LrGylcXsHWdkCe/xBX+/jYLMf6nJZPk8BPzfUpiDnEKwRl05qfRfkIDusnQrlBrE+tqtcool65js7hYIzSi92O/hxbzzfsCUpqk=,iv:lUTNJkr6Zh3MQm/h7Ven4N6xVn4VeTXOEKzxd0HSsCk=,tag:Bwbi4HD9vzso6306y7EZOg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:sPh+BuT2we+d/GaMv4zPWc3rPhlMsJQC,iv:VwcHUOMaNiao+R8RBtUINffEUhutktKD6KEWLkFxyp4=,tag:SNVKLjjDv+u5XTVczs2/Uw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJVWNYRGEwVWxDSmE4bTNL\nRlZPeGZabFZZNGFsMEwzV1ZmT1pqNVk4STMwCkg5UER0Vjk3K1RMazVVYjF3SDc2\ndDZHa3VtYjRiWUJET25weXprc0JNUjAKLS0tIDdVb2xNdWxCcjhpSGtGWDV0d2ti\nZENkZGNpSTNzMVVTZVN0ZktLc2VackEKdexhI37pwcnbZbcy30k9Uo5Z7z3NLqlx\nspxJ87SzEwdStTMhiH1iYf62vcyAOTa4HwfXu97MGVPFNw13/VfgCw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:50Z",
|
||||
"mac": "ENC[AES256_GCM,data:tZRh8qj7JUnhXCfqCHJKWEFQ8XLtmo/p0C+eFIK+34enxfB5lG5Lq83wBXLa0D/nqrr58z1rLO+UVDOI5LH1jFxARBZZnUKrVJNTDHa5pUnlnVOFEOoc+R0h2E5Xw9OHaq7aDUh4fT9+gNDpguKggI5fS9KqRnmZ4VrpNccjnkw=,iv:2yI25fcWMog91EMD7bYQy3GS30a7gZHnif93MaE3sZo=,tag:tYqa6zssiU3BCFU5xmDYZQ==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -147,25 +147,7 @@
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
clanCore = self.filter {
|
||||
include = [
|
||||
"checks/backups"
|
||||
"checks/flake-module.nix"
|
||||
"clanModules/borgbackup"
|
||||
"clanModules/flake-module.nix"
|
||||
"clanModules/localbackup"
|
||||
"clanModules/packages"
|
||||
"clanModules/single-disk"
|
||||
"clanModules/zerotier"
|
||||
"flake.lock"
|
||||
"flakeModules"
|
||||
"inventory.json"
|
||||
"nixosModules"
|
||||
# Just include everything in 'lib'
|
||||
# If anything changes in /lib that may affect everything
|
||||
"lib"
|
||||
];
|
||||
};
|
||||
clanCore = self.checks.x86_64-linux.clan-core-for-checks;
|
||||
in
|
||||
{
|
||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
||||
@@ -182,11 +164,6 @@
|
||||
# import the inventory generated nixosModules
|
||||
self.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
||||
clan.core.settings.directory = ./.;
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "foo" ''
|
||||
echo ${clanCore}
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
|
||||
6
checks/clan-core-for-checks.nix
Normal file
6
checks/clan-core-for-checks.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{ fetchgit }:
|
||||
fetchgit {
|
||||
url = "https://git.clan.lol/clan/clan-core.git";
|
||||
rev = "1e8b9def2a021877342491ca1f4c45533a580759";
|
||||
sha256 = "0f12vwr1abwa1iwjbb5z5xx8jlh80d9njwdm6iaw1z1h2m76xgzc";
|
||||
}
|
||||
122
checks/dont-depend-on-repo-root.nix
Normal file
122
checks/dont-depend-on-repo-root.nix
Normal file
@@ -0,0 +1,122 @@
|
||||
{
|
||||
...
|
||||
}:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
system,
|
||||
pkgs,
|
||||
self',
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
clanCore = self'.packages.clan-core-flake;
|
||||
clanCoreHash = lib.substring 0 12 (builtins.hashString "sha256" "${clanCore}");
|
||||
/*
|
||||
construct a flake for the test which contains a single check which depends
|
||||
on all checks of clan-core.
|
||||
*/
|
||||
testFlakeFile = pkgs.writeText "flake.nix" ''
|
||||
{
|
||||
inputs.clan-core.url = path:///to/nowhere;
|
||||
outputs = {clan-core, ...}:
|
||||
let
|
||||
checks =
|
||||
builtins.removeAttrs
|
||||
clan-core.checks.${system}
|
||||
[
|
||||
"dont-depend-on-repo-root"
|
||||
"package-dont-depend-on-repo-root"
|
||||
"package-clan-core-flake"
|
||||
];
|
||||
checksOutPaths = map (x: "''${x}") (builtins.attrValues checks);
|
||||
in
|
||||
{
|
||||
checks.${system}.check = builtins.derivation {
|
||||
name = "all-clan-core-checks";
|
||||
system = "${system}";
|
||||
builder = "/bin/sh";
|
||||
args = ["-c" '''
|
||||
of outPath in ''${toString checksOutPaths}; do
|
||||
echo "$outPath" >> $out
|
||||
done
|
||||
'''];
|
||||
};
|
||||
};
|
||||
}
|
||||
'';
|
||||
in
|
||||
lib.optionalAttrs (system == "x86_64-linux") {
|
||||
packages.dont-depend-on-repo-root =
|
||||
pkgs.runCommand
|
||||
# append repo hash to this tests name to ensure it gets invalidated on each chain
|
||||
# This is needed because this test is an FOD (due to networking) and would get cached indefinitely.
|
||||
"check-dont-depend-on-repo-root-${clanCoreHash}"
|
||||
{
|
||||
buildInputs = [
|
||||
pkgs.nix
|
||||
pkgs.cacert
|
||||
pkgs.nix-diff
|
||||
];
|
||||
outputHashAlgo = "sha256";
|
||||
outputHash = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
|
||||
}
|
||||
''
|
||||
mkdir clanCore testFlake store
|
||||
clanCore=$(realpath clanCore)
|
||||
testFlake=$(realpath testFlake)
|
||||
|
||||
# copy clan core flake and make writable
|
||||
cp -r ${clanCore}/* clanCore/
|
||||
chmod +w -R clanCore\
|
||||
|
||||
# copy test flake and make writable
|
||||
cp ${testFlakeFile} testFlake/flake.nix
|
||||
chmod +w -R testFlake
|
||||
|
||||
# enable flakes
|
||||
export NIX_CONFIG="experimental-features = nix-command flakes"
|
||||
|
||||
# give nix a $HOME
|
||||
export HOME=$(realpath ./store)
|
||||
|
||||
# override clan-core flake input to point to $clanCore\
|
||||
echo "locking clan-core to $clanCore"
|
||||
nix flake lock --override-input clan-core "path://$clanCore" "$testFlake" --store "$HOME"
|
||||
|
||||
# evaluate all tests
|
||||
echo "evaluating all tests for clan core"
|
||||
nix eval "$testFlake"#checks.${system}.check.drvPath --store "$HOME" --raw > drvPath1 &
|
||||
|
||||
# slightly modify clan core
|
||||
cp -r $clanCore clanCore2
|
||||
cp -r $testFlake testFlake2
|
||||
export clanCore2=$(realpath clanCore2)
|
||||
export testFlake2=$(realpath testFlake2)
|
||||
touch clanCore2/fly-fpv
|
||||
|
||||
# re-evaluate all tests
|
||||
echo "locking clan-core to $clanCore2"
|
||||
nix flake lock --override-input clan-core "path://$clanCore2" "$testFlake2" --store "$HOME"
|
||||
echo "evaluating all tests for clan core with added file"
|
||||
nix eval "$testFlake2"#checks.${system}.check.drvPath --store "$HOME" --raw > drvPath2
|
||||
|
||||
# wait for first nix eval to return as well
|
||||
while ! grep -q drv drvPath1; do sleep 1; done
|
||||
|
||||
# raise error if outputs are different
|
||||
if [ "$(cat drvPath1)" != "$(cat drvPath2)" ]; then
|
||||
echo -e "\n\nERROR: Something in clan-core depends on the whole repo" > /dev/stderr
|
||||
echo -e "See details in the nix-diff below which shows the difference between two evaluations:"
|
||||
echo -e " 1. Evaluation of clan-core checks without any changes"
|
||||
echo -e " 1. Evaluation of clan-core checks after adding a file to the top-level of the repo"
|
||||
echo "nix-diff:"
|
||||
export NIX_REMOTE="$HOME"
|
||||
nix-diff $(cat drvPath1) $(cat drvPath2)
|
||||
exit 1
|
||||
fi
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -26,6 +26,7 @@ clanLib.test.makeTestClan {
|
||||
roles.admin.machines = [ "admin1" ];
|
||||
};
|
||||
};
|
||||
|
||||
instances."test" = {
|
||||
module.name = "new-service";
|
||||
roles.peer.machines.peer1 = { };
|
||||
@@ -33,25 +34,33 @@ clanLib.test.makeTestClan {
|
||||
|
||||
modules = {
|
||||
legacy-module = ./legacy-module;
|
||||
new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
# ./pkgs/scripts/update-vars.py
|
||||
clan.core.vars.generators.new-service = {
|
||||
files.hello = {
|
||||
secret = false;
|
||||
deploy = true;
|
||||
};
|
||||
script = ''
|
||||
# This is a dummy script that does nothing
|
||||
echo "This is a dummy script" > $out/hello
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
# nix run .#generate-test-vars -- checks/dummy-inventory-test dummy-inventory-test
|
||||
clan.core.vars.generators.new-service = {
|
||||
files.not-a-secret = {
|
||||
secret = false;
|
||||
deploy = true;
|
||||
};
|
||||
files.a-secret = {
|
||||
secret = true;
|
||||
deploy = true;
|
||||
owner = "nobody";
|
||||
group = "users";
|
||||
mode = "0644";
|
||||
};
|
||||
script = ''
|
||||
# This is a dummy script that does nothing
|
||||
echo -n "not-a-secret" > $out/not-a-secret
|
||||
echo -n "a-secret" > $out/a-secret
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -69,7 +78,15 @@ clanLib.test.makeTestClan {
|
||||
print(peer1.succeed("systemctl status dummy-service"))
|
||||
|
||||
# peer1 should have the 'hello' file
|
||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.hello.path}")
|
||||
peer1.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}"
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1hd2exjq88h7538y6mvjvexx3u5gp6a03yfn5nj32h2667yyksyaqcuk5qs",
|
||||
"publickey": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age19urkt89q45a2wk6a4yaramzufjtnw6nq2snls0v7hmf7tqf73axsfx50tk",
|
||||
"publickey": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:hhuFgZcPqht0h3tKxGtheS4GlrVDo4TxH0a9lxgPYj2i12QUmE04rB07A+hu4Z8WNWLYvdM5069mEOZYm3lSeTzBHQPxYZRuVj0=,iv:sA1srRFQqsMlJTAjFcb09tI/Jg2WjOVJL5NZkPwiLoU=,tag:6xXo9FZpmAJw6hCBsWzf8Q==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:GPpsUhSzWPtTP8EUNKsobFXjYqDldhkkIH6hBk11RsDLAGWdhVrwcISGbhsWpYhvAdPKA84DB6Zqyh9lL2bLM9//ybC1kzY20BQ=,iv:NrxMLdedT2FCkUAD00SwsAHchIsxWvqe7BQekWuJcxw=,tag:pMDXcMyHnLF2t3Qhb1KolA==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGaGVHeTgrN3dJQ2VITFBM\neWVzbDhjb0pwNUhBUjdUc0p5OTVta1dvSno4ClJxeUc4Z0hiaFRkVlJ1YTA4Lyta\neWdwV005WGYvMUNRVG1qOVdicTk0NUkKLS0tIFQvaDNFS1JMSFlHRXlhc3lsZm03\nYVhDaHNsam5wN1VqdzA3WTZwM1JwV2sKZk/SiZJgjllADdfHLSWuQcU4+LttDpt/\nqqDUATEuqYaALljC/y3COT+grTM2bwGjj6fsfsfiO/EL9iwzD3+7oA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzb2tWb1ExKzdmUTRzaGVj\nK3cyYTBHZTJwVjM1SzUvbHFiMnVhY05iKzFZCnJTSE1VSVdpcUFLSEJuaE1CZzJD\nWjZxYzN2cUltdThNMVRKU3FIb20vUXMKLS0tIFlHQXRIdnMybDZFUVEzWlQrc1dw\nbUxhZURXblhHd0pka0JIK1FTZEVqdUEKI/rfxQRBc+xGRelhswkJQ9GcZs6lzfgy\nuCxS5JI9npdPLQ/131F3b21+sP5YWqks41uZG+vslM1zQ+BlENNhDw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:10:16Z",
|
||||
"mac": "ENC[AES256_GCM,data:xuXj4833G6nhvcRo2ekDxz8G5phltmU8h1GgGofH9WndzrqLKeRSqm/n03IHRW0f4F68XxnyAkfvokVh6vW3LRQAFkqIlXz5U4+zFNcaVaPobS5gHTgxsCoTUoalWPvHWtXd50hUVXeAt8rPfTfeveVGja8bOERk8mvwUPxb6h4=,iv:yP1usA9m8tKl6Z/UK9PaVMJlZlF5qpY4EiM4+ByVlik=,tag:8DgoIhLstp3MRki90VfEvw==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:13Z",
|
||||
"mac": "ENC[AES256_GCM,data:fWxLHXBWolHVxv6Q7utcy6OVLV13ziswrIYyNKiwy1vsU8i7xvvuGO1HlnE+q43D2WuHR53liKq1UHuf1JMrWzTwZ0PYe+CVugtoEtbR2qu3rK/jAkOyMyhmmHzmf6Rp4ZMCzKgZeC/X2bDKY/z0firHAvjWydEyogutHpvtznM=,iv:OQI3FfkLneqbdztAXVQB3UkHwDPK+0hWu5hZ9m8Oczg=,tag:em6GfS2QHsXs391QKPxfmA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:rwPhbayGf6mE1E9NCN+LuL7VfWWOfhoJW6H2tNSoyebtyTpM3GO2jWca1+N7hI0juhNkUk+rIsYQYbCa/5DZQiV0/2Jgu4US1XY=,iv:B5mcaQsDjb6BacxGB4Kk88/qLCpVOjQNRvGN+fgUiEo=,tag:Uz0A8kAF5NzFetbv9yHIjQ==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:W3cOkUYL5/YulW2pEISyTlMaA/t7/WBE7BoCdFlqrqgaCL7tG4IV2HgjiPWzIVMs0zvDSaghdEvAIoB4wOf470d1nSWs0/E8SDk=,iv:wXXaZIw3sPY8L/wxsu7+C5v+d3RQRuwxZRP4YLkS8K4=,tag:HeK4okj7O7XDA9JDz2KULw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWY0hKQ1dnV0tMYytDMCtj\nTDV4Zk5NeVN0bCtqaWRQV3d4M0VlcGVZMkhZCm02dHZyOGVlYzJ5Z3FlUWNXMVQ0\nb2ZrTXZQRzRNdzFDeWZCVGhlTS9rMm8KLS0tIEJkY1QwOENRYWw3cjIwd3I0bzdz\nOEtQNm1saE5wNWt2UUVnYlN4NWtGdFkKmWHU5ttZoQ3NZu/zkX5VxfC2sMpSOyod\neb7LRhFqPfo5N1XphJcCqr5QUoZOfnH0xFhZ2lxWUS3ItiRpU4VDwg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxRC83b3dtSVpXcGovNnVs\nTzFka2J2MEFhYkF1ajVrdjMrNUtPWGRObjM4Cm5zSUR5OGw0T0FaL3BaWmR6L29W\nU2syMFIyMUhFRUZpWFpCT28vWko2ZU0KLS0tIFpHK3BjU1V1L0FrMGtwTGFuU3Mz\nRkV5VjI2Vndod202bUR3RWQwNXpmVzQKNk8/y7M62wTIIKqY4r3ZRk5aUCRUfine\n1LUSHMKa2bRe+hR7nS7AF4BGXp03h2UPY0FP5+U5q8XuIj1jfMX8kg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:10:41Z",
|
||||
"mac": "ENC[AES256_GCM,data:pab0G2GPjgs59sbiZ8XIV5SdRtq5NPU0yq18FcqiMV8noAL94fyVAY7fb+9HILQWQsEjcykgk9mA2MQ0KpK/XG8+tDQKcBH+F+2aQnw5GJevXmfi7KLTU0P224SNo7EnKlfFruB/+NZ0WBtkbbg1OzekrbplchpSI6BxWz/jASE=,iv:TCj9FCxgfMF2+PJejr67zgGnF+CFS+YeJiejnHbf7j0=,tag:s7r9SqxeqpAkncohYvIQ2Q==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:16Z",
|
||||
"mac": "ENC[AES256_GCM,data:yTkQeFvKrN1+5FP+yInsaRWSAG+ZGG0uWF3+gVRvzJTFxab8kT2XkAMc+4D7SKgcjsmwBBb77GNoAKaKByhZ92UaCfZ2X66i7ZmYUwLM1NVVmm+xiwwjsh7PJXlZO/70anTzd1evtlZse0jEmRnV5Y0F0M6YqXmuwU+qGUJU2F8=,iv:sy6ozhXonWVruaQfa7pdEoV5GkNZR/UbbINKAPbgWeg=,tag:VMruQ1KExmlMR7TsGNgMlg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:bxM9aYMK,iv:SMNYtk9FSyZ1PIfEzayTKKdCnZWdhcyUEiTwFUNb988=,tag:qJYW4+VQyhF1tGPQPTKlOQ==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:T8edCvw=,iv:7/G5xt5fv38I9uFzk7WMIr9xQdz/6lFxqOC+18HBg8Q=,tag:F39Cxbgmzml+lZLsZ59Kmg==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1hd2exjq88h7538y6mvjvexx3u5gp6a03yfn5nj32h2667yyksyaqcuk5qs",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvZDZYYXdpcXVqRFRnQ2Jx\nTFhFWEJTR290cHZhTXZadFFvcHM4MHVIN3lFCmJhOEZrL3g4TFBZVllxdDFZakJn\nR3NxdXo0eE8vTDh3QlhWOFpVZ0lNUHcKLS0tIEE4dkpCalNzaXJ0Qks3VHJSUzZF\nb2N3NGdjNHJnSUN6bW8welZ1VDdJakEKGKZ7nn1p11IyJB6DMxu2HJMvZ+0+5WpE\nPLWh2NlGJO3XrrL4Fw7xetwbqE+QUZPNl/JbEbu4KLIUGLjqk9JDhQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"recipient": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPNUhiYkZWK3dPMHNiRTVM\nRHNvaHFsOFp1c0UxQitwVG0zY01MNDZRV1E4CjEybENoTVIzN29vQ3FtUTRSYmFU\nNXIzQllVSllXRGN2M1B6WXJLdHZSajgKLS0tIDllZ0ZmZUcxMHhDQUpUOEdWbmkv\neUQweHArYTdFSmNteVpuQ3BKdnh0Y0UKs8Hm3D+rXRRfpUVSZM3zYjs6b9z8g10D\nGTkvreUMim4CS22pjdQ3eNA9TGeDXfWXE7XzwXLCb+wVcf7KwbDmvg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHckJCQVFyb21aT1R0d2Rr\nMWxNMHVqcGxabHBmS0RibW9sN0gyZDI1b1dFCnRWUk5LSWdxV3c4RWVZdUtEN1Fv\nRk4xVmwwT2xrdWVERkJXUVVlVXJjTVUKLS0tIC9ERG9KMGxTNEsrbzFHUGRiVUlm\nRi9qakxoc1FOVVV1TkUrckwxRUVnajQKE8ms/np2NMswden3xkjdC8cXccASLOoN\nu+EaEk69UvBvnOg9VBjyPAraIKgNrTc4WWwz+DOBj1pCwVbu9XxUlA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKSDhpT3cvck9PenZYVEZH\ndFQreVRBdG93L1dBUGlvYjFWcDlHWUJsZUVBCm9DMTJ4UytiYzlEVHNWdUcwS1ds\nT0dhbzAzNDdmbDBCU0dvL2xNeHpXcGsKLS0tIFArbmpsbzU3WnpJdUt1MGN0L1d0\nV1JkTDJYWUxsbmhTQVNOeVRaSUhTODQKk9Vph2eldS5nwuvVX0SCsxEm4B+sO76Z\ndIjJ3OQxzoZmXMaOOuKHC5U0Y75Qn7eXC43w5KHsl2CMIUYsBGJOZw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:10:30Z",
|
||||
"mac": "ENC[AES256_GCM,data:cIwWctUbAFI8TRMxYWy5xqlKDVLMqBIxVv4LInnLqi3AauL0rJ3Z7AxK/wb2dCQM07E1N7YaORNqgUpFC1xo0hObAA8mrPaToPotKDkjua0zuyTUNS1COoraYjZpI/LKwmik/qtk399LMhiC7aHs+IliT9Dd41B8LSMBXwdMldY=,iv:sZ+//BrYH5Ay2JJAGs7K+WfO2ASK82syDlilQjGmgFs=,tag:nY+Af9eQRLwkiHZe85dQ9A==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:14Z",
|
||||
"mac": "ENC[AES256_GCM,data:6fKrS1eLLUWlHkQpxLFXBRk6f2wa5ADLMViVvYXXGU24ayl9UuNSKrCRHp9cbzhqhti3HdwyNt6TM+2X6qhiiAQanKEB2PF7JRYX74NfNKil9BEDjt5AqqtpSgVv5l7Ku/uSHaPkd2sDmzHsy5Q4bSGxJQokStk1kidrwle+mbc=,iv:I/Aad82L/TCxStM8d8IZICUrwdjRbGx2fuGWqexr21o=,tag:BfgRbGUxhPZzK2fLik1kxA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
13898
|
||||
18650
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:ImlGIKxE,iv:UUWxjLNRKJCD2WHNpw8lfvCc8rnXPCqc2pni1ODckjE=,tag:HFCqiv31E9bShIIaAEjF0A==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:vp0yW0Gt,iv:FO2cy+UpEl5aRay/LUGu//c82QiVxuKuGSaVh0rGJvc=,tag:vf2RAOPpcRW0HwxHoGy17A==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age19urkt89q45a2wk6a4yaramzufjtnw6nq2snls0v7hmf7tqf73axsfx50tk",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpTEROZjh6NjBhSlJSc1Av\nSHhjdkhwVUd3VzBZemhQb3dhMlJXalBmZlFjCkZPYkhZZGVOVTNjUWdFU0s4cWFn\nL2NXbkRCdUlMdElnK2lGbG5iV0w1cHMKLS0tIFREcmxDdHlUNVBFVGRVZSt0c0E5\nbnpHaW1Vb3R3ZFFnZVMxY3djSjJmOU0KIwqCSQf5S9oA59BXu7yC/V6yqvCh88pa\nYgmNyBjulytPh1aAfOuNWIGdIxBpcEf+gFjz3EiJY9Kft3fTmhp2bw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjaFVNMEd2YUxpSm5XVVRi\nY2ZUc3NTOStJUFNMWWVPQTgxZ2tCK1QrMW1ZCjYwMlA4dkIzSlc0TGtvZjcyK3Bi\nM3pob2JOOFUyeVJ6M2JpaTRCZlc1R0kKLS0tIDJMb1dFcVRWckhwYWNCQng0RlFO\nTkw3OGt4dkFIZVY5aVEzZE5mMzJSM0EKUv8bUqg48L2FfYVUVlpXvyZvPye699of\nG6PcjLh1ZMbNCfnsCzr+P8Vdk/F4J/ifxL66lRGfu2xOLxwciwQ+5Q==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArN3R4TThibjdYbE9TMDE1\naUhuNDlscExjaktIR2VmTk1OMWtVM0NpTUJZClJUNEcwVDlibExWQk84TTNEWFhp\nMjYyZStHc1N0ZTh1S3VTVk45WGxlWWMKLS0tIHFab25LY1R1d1l6NE5XbHJvQ3lj\nNGsxUldFVHQ5RVJERDlGbi9NY29hNWsKENBTcAS/R/dTGRYdaWv5Mc/YG4bkah5w\nb421ZMQF+r4CYnzUqnwivTG8TMRMqJLavfkutE6ZUfJbbLufrTk5Lw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnZ2dDbVhoQngxM3lTSmZF\nUTAwS1lCTGhEMU1GVXpFUzlIUFdqZy9LajF3Ck9mdVpBRjlyVUNhZXZIUFZjUzF1\nNlhFN28vNmwzcUVkNmlzUnpkWjJuZE0KLS0tIHpXVHVlNk9vU1ZPTGRrYStWbmRO\nbDM4U2o1SlEwYWtqOXBqd3BFUTAvMHcKkI8UVd0v+x+ELZ5CoGq9DzlA6DnVNU2r\nrV9wLfbFd7RHxS0/TYZh5tmU42nO3iMYA9FqERQXCtZgXS9KvfqHwQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:11:04Z",
|
||||
"mac": "ENC[AES256_GCM,data:JdJzocQZWVprOmZ4Ni04k1tpD1TpFcK5neKy3+0/c3+uPBwjwaMayISKRaa/ILUXlalg60oTqxB4fUFoYVm8KGQVhDwPhO/T1hyYVQqidonrcYfJfCYg00mVSREV/AWqXb7RTnaEBfrdnRJvaAQF9g2qDXGVgzp3eACdlItclv4=,iv:nOw1jQjIWHWwU3SiKpuQgMKXyu8MZYI+zI9UYYd9fCI=,tag:ewUkemIPm/5PkmuUD0EcAQ==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:18Z",
|
||||
"mac": "ENC[AES256_GCM,data:1ZZ+ZI1JsHmxTov1bRijfol3kTkyheg2o3ivLsMHRhCmScsUry97hQJchF78+y2Izt7avaQEHYn6pVbYt/0rLrSYD7Ru7ITVxXoYHOiN5Qb98masUzpibZjrdyg5nO+LW5/Hmmwsc3yn/+o3IH1AUYpsxlJRdnHHCmoSOFaiFFM=,iv:OQlgmpOTw4ljujNzqwQ5/0Mz8pQpCSUtqRvj3FJAxDs=,tag:foZvdeW7gK9ZVKkWqnlxGA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
30661
|
||||
6745
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/peer1
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:prFl0EJy8bM=,iv:zITWxf+6Ebk0iB5vhhd7SBQa1HFrIJXm8xpSM+D9I0M=,tag:NZCRMCs1SzNKLBu/KUDKMQ==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0S0RZRWxaZVZvTUhjdWVL\naU9WZmtEcm1qa2JsRmdvdmZmNENMaWFEVUFRCmdoVnRXSGlpRlFjNmVVbDJ5VnFT\nMnVJUlVnM3lxNmZCRTdoRVJ4NW1oYWcKLS0tIFFNbXBFUk1RWnlUTW1SeG1vYzlM\nVVpEclFVOE9PWWQxVkZ0eEgwWndoRWcKDAOHe+FIxqGsc6LhxMy164qjwG6t2Ei2\nP0FSs+bcKMDpudxeuxCjnDm/VoLxOWeuqkB+9K2vSm2W/c/fHTSbrA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2VU5jOEpwYUtDVEVFcVpU\nQkExTVZ3ejZHcGo5TG8zdUQwNktoV09WdUZvCmQ0dE1TOWRFbTlxdVd4WWRxd3VF\nQUNTTkNNT3NKYjQ5dEJDY0xVZ3pZVUUKLS0tIDFjajRZNFJZUTdNeS8yN05FMFZU\ncEtjRjhRbGE0MnRLdk10NkFLMkxqencKGzJ66dHluIghH04RV/FccfEQP07yqnfb\n25Hi0XIVJfXBwje4UEyszrWTxPPwVXdQDQmoNKf76Qy2jYqJ56uksw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-04T12:44:20Z",
|
||||
"mac": "ENC[AES256_GCM,data:FIkilsni5kOdNlVwDuLsQ/zExypHRWdqIBQDNWMLTwe8OrsNPkX+KYutUvt9GaSoGv4iDULaMRoizO/OZUNfc2d8XYSdj0cxOG1Joov4GPUcC/UGyNuQneAejZBKolvlnidKZArofnuK9g+lOTANEUtEXUTnx8L+VahqPZayQas=,iv:NAo6sT3L8OOB3wv1pjr3RY2FwXgVmZ4N0F4BEX4YPUY=,tag:zHwmXygyvkdpASZCodQT9Q==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
This is a dummy script
|
||||
@@ -0,0 +1 @@
|
||||
not-a-secret
|
||||
@@ -14,12 +14,14 @@ in
|
||||
./installation/flake-module.nix
|
||||
./morph/flake-module.nix
|
||||
./nixos-documentation/flake-module.nix
|
||||
./dont-depend-on-repo-root.nix
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
self',
|
||||
system,
|
||||
...
|
||||
}:
|
||||
{
|
||||
@@ -32,33 +34,45 @@ in
|
||||
inherit self;
|
||||
inherit (self) clanLib;
|
||||
};
|
||||
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
# Deltachat is currently marked as broken
|
||||
# deltachat = import ./deltachat nixosTestArgs;
|
||||
nixosTests =
|
||||
lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
# Deltachat is currently marked as broken
|
||||
# deltachat = import ./deltachat nixosTestArgs;
|
||||
|
||||
# Base Tests
|
||||
secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
|
||||
borgbackup = self.clanLib.test.baseTest ./borgbackup nixosTestArgs;
|
||||
wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||
# Base Tests
|
||||
secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
|
||||
borgbackup = self.clanLib.test.baseTest ./borgbackup nixosTestArgs;
|
||||
wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||
|
||||
# Container Tests
|
||||
container = self.clanLib.test.containerTest ./container nixosTestArgs;
|
||||
zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
|
||||
matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
|
||||
postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs;
|
||||
# Container Tests
|
||||
container = self.clanLib.test.containerTest ./container nixosTestArgs;
|
||||
zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
|
||||
matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
|
||||
postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs;
|
||||
|
||||
# Clan Tests
|
||||
mumble = import ./mumble nixosTestArgs;
|
||||
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
|
||||
data-mesher = import ./data-mesher nixosTestArgs;
|
||||
syncthing = import ./syncthing nixosTestArgs;
|
||||
};
|
||||
# Clan Tests
|
||||
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
|
||||
admin = import ./admin nixosTestArgs;
|
||||
data-mesher = import ./data-mesher nixosTestArgs;
|
||||
syncthing = import ./syncthing nixosTestArgs;
|
||||
}
|
||||
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "aarch64-linux") {
|
||||
# for some reason this hangs in an odd place in CI, but it works on my machine ...
|
||||
# on aarch64-linux it works though
|
||||
mumble = import ./mumble nixosTestArgs;
|
||||
};
|
||||
|
||||
packagesToBuild = lib.removeAttrs self'.packages [
|
||||
# exclude the check that checks that nothing depends on the repo root
|
||||
# We might want to include this later once everything is fixed
|
||||
"dont-depend-on-repo-root"
|
||||
];
|
||||
|
||||
flakeOutputs =
|
||||
lib.mapAttrs' (
|
||||
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
|
||||
) (lib.filterAttrs (n: _: !lib.hasPrefix "test-" n) self.nixosConfigurations)
|
||||
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
|
||||
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") packagesToBuild
|
||||
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
|
||||
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
|
||||
self'.legacyPackages.homeConfigurations or { }
|
||||
@@ -76,7 +90,10 @@ in
|
||||
schema =
|
||||
(self.clanLib.inventory.evalClanService {
|
||||
modules = [ m ];
|
||||
key = "checks";
|
||||
prefix = [
|
||||
"checks"
|
||||
system
|
||||
];
|
||||
}).config.result.api.schema;
|
||||
in
|
||||
schema
|
||||
@@ -90,6 +107,12 @@ in
|
||||
mkdir -p $out
|
||||
cat $schemaFile > $out/allSchemas.json
|
||||
'';
|
||||
|
||||
clan-core-for-checks = pkgs.runCommand "clan-core-for-checks" { } ''
|
||||
cp -r ${pkgs.callPackage ./clan-core-for-checks.nix { }} $out
|
||||
chmod +w $out/flake.lock
|
||||
cp ${../flake.lock} $out/flake.lock
|
||||
'';
|
||||
};
|
||||
legacyPackages = {
|
||||
nixosTests =
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
let
|
||||
dependencies = [
|
||||
pkgs.disko
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.ConfigIniFiles
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.FileSlurp
|
||||
|
||||
@@ -80,7 +81,7 @@
|
||||
|
||||
# Some distros like to automount disks with spaces
|
||||
machine.succeed('mkdir -p "/mnt/with spaces" && mkfs.ext4 /dev/vdb && mount /dev/vdb "/mnt/with spaces"')
|
||||
machine.succeed("clan flash write --debug --flake ${../..} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
|
||||
machine.succeed("clan flash write --debug --flake ${self.checks.x86_64-linux.clan-core-for-checks} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@ let
|
||||
{ modulesPath, pkgs, ... }:
|
||||
let
|
||||
dependencies = [
|
||||
self
|
||||
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.diskoScript
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.clan.deployment.file
|
||||
@@ -16,6 +15,7 @@ let
|
||||
pkgs.bash.drvPath
|
||||
pkgs.nixos-anywhere
|
||||
pkgs.bubblewrap
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
in
|
||||
@@ -198,7 +198,7 @@ in
|
||||
installer.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../assets/ssh/privkey} /root/.ssh/id_ed25519")
|
||||
|
||||
installer.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new -v nonrootuser@localhost hostname")
|
||||
installer.succeed("cp -r ${../..} test-flake && chmod -R +w test-flake")
|
||||
installer.succeed("cp -r ${self.checks.x86_64-linux.clan-core-for-checks} test-flake && chmod -R +w test-flake")
|
||||
|
||||
installer.succeed("clan machines install --no-reboot --debug --flake test-flake --yes test-install-machine-without-system --target-host nonrootuser@localhost --update-hardware-config nixos-facter >&2")
|
||||
installer.shutdown()
|
||||
@@ -218,7 +218,7 @@ in
|
||||
installer.start()
|
||||
installer.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../assets/ssh/privkey} /root/.ssh/id_ed25519")
|
||||
installer.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new -v nonrootuser@localhost hostname")
|
||||
installer.succeed("cp -r ${../..} test-flake && chmod -R +w test-flake")
|
||||
installer.succeed("cp -r ${self.checks.x86_64-linux.clan-core-for-checks} test-flake && chmod -R +w test-flake")
|
||||
installer.fail("test -f test-flake/machines/test-install-machine/hardware-configuration.nix")
|
||||
installer.fail("test -f test-flake/machines/test-install-machine/facter.json")
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
dependencies = [
|
||||
self
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.stdenvNoCC
|
||||
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
|
||||
@@ -44,14 +43,18 @@
|
||||
{
|
||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||
system.extraDependencies = dependencies;
|
||||
|
||||
virtualisation.memorySize = 2048;
|
||||
virtualisation.useNixStoreImage = true;
|
||||
virtualisation.writableStore = true;
|
||||
|
||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli-full ];
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
start_all()
|
||||
actual.fail("cat /etc/testfile")
|
||||
actual.succeed("env CLAN_DIR=${self} clan machines morph test-morph-template --i-will-be-fired-for-using-this --debug --name test-morph-machine")
|
||||
actual.succeed("env CLAN_DIR=${self.checks.x86_64-linux.clan-core-for-checks} clan machines morph test-morph-template --i-will-be-fired-for-using-this --debug --name test-morph-machine")
|
||||
assert actual.succeed("cat /etc/testfile") == "morphed"
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
(modulesPath + "/profiles/minimal.nix")
|
||||
];
|
||||
|
||||
virtualisation.useNixStoreImage = true;
|
||||
virtualisation.writableStore = true;
|
||||
|
||||
clan.core.enableRecommendedDefaults = false;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,20 @@ clanLib.test.makeTestClan {
|
||||
nodes.peer2 = common;
|
||||
|
||||
testScript = ''
|
||||
import time
|
||||
import re
|
||||
|
||||
|
||||
def machine_has_text(machine: Machine, regex: str) -> bool:
|
||||
variants = machine.get_screen_text_variants()
|
||||
# for debugging
|
||||
# machine.screenshot(f"/tmp/{machine.name}.png")
|
||||
for text in variants:
|
||||
print(f"Expecting '{regex}' in '{text}'")
|
||||
if re.search(regex, text) is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
start_all()
|
||||
|
||||
with subtest("Waiting for x"):
|
||||
@@ -63,41 +77,53 @@ clanLib.test.makeTestClan {
|
||||
peer2.execute("mumble >&2 &")
|
||||
|
||||
with subtest("Wait for Mumble"):
|
||||
peer1.wait_for_window(r"^Mumble$")
|
||||
peer2.wait_for_window(r"^Mumble$")
|
||||
peer1.wait_for_window(r"Mumble")
|
||||
peer2.wait_for_window(r"Mumble")
|
||||
|
||||
with subtest("Wait for certificate creation"):
|
||||
peer1.wait_for_window(r"^Mumble$")
|
||||
peer1.sleep(3) # mumble is slow to register handlers
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
peer2.wait_for_window(r"^Mumble$")
|
||||
peer2.sleep(3) # mumble is slow to register handlers
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
peer1.wait_for_window(r"Mumble")
|
||||
peer2.wait_for_window(r"Mumble")
|
||||
|
||||
with subtest("Wait for server connect"):
|
||||
peer1.wait_for_window(r"^Mumble Server Connect$")
|
||||
peer2.wait_for_window(r"^Mumble Server Connect$")
|
||||
for i in range(20):
|
||||
time.sleep(1)
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
if machine_has_text(peer1, r"Mumble Server Connect") and \
|
||||
machine_has_text(peer2, r"Mumble Server Connect"):
|
||||
break
|
||||
else:
|
||||
raise Exception("Timeout waiting for certificate creation")
|
||||
|
||||
with subtest("Check validity of server certificates"):
|
||||
peer1.execute("killall .mumble-wrapped")
|
||||
peer1.sleep(1)
|
||||
peer1.execute("mumble mumble://peer2 >&2 &")
|
||||
peer1.wait_for_window(r"^Mumble$")
|
||||
peer1.sleep(3) # mumble is slow to register handlers
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
peer1.wait_for_text("Connected.")
|
||||
peer1.wait_for_window(r"Mumble")
|
||||
|
||||
for i in range(20):
|
||||
time.sleep(1)
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
if machine_has_text(peer1, "Connected."):
|
||||
break
|
||||
else:
|
||||
raise Exception("Timeout waiting for certificate creation")
|
||||
|
||||
peer2.execute("killall .mumble-wrapped")
|
||||
peer2.sleep(1)
|
||||
peer2.execute("mumble mumble://peer1 >&2 &")
|
||||
peer2.wait_for_window(r"^Mumble$")
|
||||
peer2.sleep(3) # mumble is slow to register handlers
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
peer2.wait_for_text("Connected.")
|
||||
peer2.wait_for_window(r"Mumble")
|
||||
|
||||
for i in range(20):
|
||||
time.sleep(1)
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
if machine_has_text(peer2, "Connected."):
|
||||
break
|
||||
else:
|
||||
raise Exception("Timeout waiting for certificate creation")
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# Dont import this file
|
||||
# It is only here for backwards compatibility.
|
||||
# Dont author new modules with this file.
|
||||
{
|
||||
imports = [ ./roles/default.nix ];
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{ lib, config, ... }:
|
||||
{
|
||||
options.clan.admin = {
|
||||
allowedKeys = lib.mkOption {
|
||||
default = { };
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
description = "The allowed public keys for ssh access to the admin user";
|
||||
example = {
|
||||
"key_1" = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
|
||||
};
|
||||
};
|
||||
};
|
||||
# Bad practice.
|
||||
# Should we add 'clanModules' to specialArgs?
|
||||
imports = [
|
||||
../../sshd
|
||||
../../root-password
|
||||
];
|
||||
config = {
|
||||
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues config.clan.admin.allowedKeys;
|
||||
};
|
||||
}
|
||||
@@ -105,10 +105,7 @@ in
|
||||
private_key = {
|
||||
inherit owner;
|
||||
};
|
||||
public_key = {
|
||||
inherit owner;
|
||||
secret = false;
|
||||
};
|
||||
public_key.secret = false;
|
||||
};
|
||||
|
||||
runtimeInputs = [
|
||||
@@ -134,10 +131,7 @@ in
|
||||
private_key = {
|
||||
inherit owner;
|
||||
};
|
||||
public_key = {
|
||||
inherit owner;
|
||||
secret = false;
|
||||
};
|
||||
public_key.secret = false;
|
||||
};
|
||||
|
||||
runtimeInputs = [
|
||||
|
||||
@@ -210,14 +210,18 @@ in
|
||||
data_dir = Path('data')
|
||||
data_dir.mkdir(mode=0o770, exist_ok=True)
|
||||
|
||||
# Create a temporary config file
|
||||
# with appropriate permissions
|
||||
tmp_config_path = data_dir / '.config.json'
|
||||
tmp_config_path.touch(mode=0o660, exist_ok=False)
|
||||
|
||||
# Write the config with secrets back
|
||||
config_path = data_dir / 'config.json'
|
||||
with open(config_path, 'w') as f:
|
||||
with open(tmp_config_path, 'w') as f:
|
||||
f.write(json.dumps(config, indent=4))
|
||||
|
||||
# Set file permissions to read and write
|
||||
# only by the user and group
|
||||
config_path.chmod(0o660)
|
||||
# Move config into place
|
||||
config_path = data_dir / 'config.json'
|
||||
tmp_config_path.rename(config_path)
|
||||
|
||||
# Set file permissions to read
|
||||
# and write only by the user and group
|
||||
|
||||
@@ -8,7 +8,6 @@ in
|
||||
{
|
||||
# only import available files, as this allows to filter the files for tests.
|
||||
flake.clanModules = filterAttrs (_name: pathExists) {
|
||||
admin = ./admin;
|
||||
auto-upgrade = ./auto-upgrade;
|
||||
borgbackup = ./borgbackup;
|
||||
borgbackup-static = ./borgbackup-static;
|
||||
|
||||
@@ -7,8 +7,12 @@ features = [ "inventory" ]
|
||||
After the system was installed/deployed the following command can be used to display the root-password:
|
||||
|
||||
```bash
|
||||
clan secrets get {machine_name}-password
|
||||
clan vars get [machine_name] root-password/root-password
|
||||
```
|
||||
|
||||
See also: [Vars](../../manual/vars-backend.md)
|
||||
|
||||
See also: [Facts / Secrets](../../getting-started/secrets.md)
|
||||
To regenerate the password run:
|
||||
```
|
||||
clan vars generate --regenerate [machine_name] --generator root-password
|
||||
```
|
||||
|
||||
@@ -13,9 +13,12 @@ If setting the option prompt to true, the user will be prompted to type in their
|
||||
After the system was installed/deployed the following command can be used to display the user-password:
|
||||
|
||||
```bash
|
||||
clan secrets get {machine_name}-user-password
|
||||
clan vars get [machine_name] root-password/root-password
|
||||
```
|
||||
|
||||
See also: [Facts / Secrets](../../getting-started/secrets.md)
|
||||
See also: [Vars](../../manual/vars-backend.md)
|
||||
|
||||
To regenerate the password, delete the password files in the clan directory and redeploy the machine.
|
||||
To regenerate the password run:
|
||||
```
|
||||
clan vars generate --regenerate [machine_name] --generator user-password
|
||||
```
|
||||
|
||||
37
clanServices/admin/default.nix
Normal file
37
clanServices/admin/default.nix
Normal file
@@ -0,0 +1,37 @@
|
||||
{ ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/admin";
|
||||
|
||||
roles.default = {
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options.allowedKeys = lib.mkOption {
|
||||
default = { };
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
description = "The allowed public keys for ssh access to the admin user";
|
||||
example = {
|
||||
"key_1" = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
../../clanModules/sshd
|
||||
../../clanModules/root-password
|
||||
];
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues settings.allowedKeys;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
imports = [
|
||||
./hello-world/flake-module.nix
|
||||
];
|
||||
|
||||
clan.modules = {
|
||||
admin = lib.modules.importApply ./admin/default.nix { };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,15 @@
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/hello-word";
|
||||
|
||||
roles.peer = { };
|
||||
roles.peer = {
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options.foo = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perMachine =
|
||||
{ machine, ... }:
|
||||
|
||||
@@ -10,9 +10,6 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
clan.inventory.modules = {
|
||||
hello-world = module;
|
||||
};
|
||||
clan.modules = {
|
||||
hello-world = module;
|
||||
};
|
||||
@@ -50,6 +47,7 @@ in
|
||||
hello-service = import ./tests/vm/default.nix {
|
||||
inherit module;
|
||||
inherit self inputs pkgs;
|
||||
# clanLib is exposed from inputs.clan-core
|
||||
clanLib = self.clanLib;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ let
|
||||
};
|
||||
|
||||
# Register the module for the test
|
||||
inventory.modules.hello-world = module;
|
||||
modules.hello-world = module;
|
||||
|
||||
# Use the module in the test
|
||||
inventory.instances = {
|
||||
|
||||
@@ -14,6 +14,9 @@ clanLib.test.makeTestClan {
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
modules = {
|
||||
hello-service = module;
|
||||
};
|
||||
inventory = {
|
||||
machines.peer1 = { };
|
||||
|
||||
@@ -21,10 +24,6 @@ clanLib.test.makeTestClan {
|
||||
module.name = "hello-service";
|
||||
roles.peer.machines.peer1 = { };
|
||||
};
|
||||
|
||||
modules = {
|
||||
hello-service = module;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ We might not be sure whether all of those will exist but the architecture should
|
||||
## Decision
|
||||
|
||||
This leads to the conclusion that we should do `library` centric development.
|
||||
With the current `clan` python code beeing a library that can be imported to create various tools ontop of it.
|
||||
With the current `clan` python code being a library that can be imported to create various tools ontop of it.
|
||||
All **CLI** or **UI** related parts should be moved out of the main library.
|
||||
|
||||
*Note: The next person who wants implement any new frontend should do this first. Currently it looks like the TUI is the next one.*
|
||||
|
||||
47
decisions/03-adr-numbering-process.md
Normal file
47
decisions/03-adr-numbering-process.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# ADR Numbering process
|
||||
|
||||
## Status
|
||||
|
||||
Proposed after some conversation between @lassulus, @Mic92, & @lopter.
|
||||
|
||||
## Context
|
||||
|
||||
It can be useful to refer to ADRs by their numbers, rather than their full title. To that end, short and sequential numbers are useful.
|
||||
|
||||
The issue is that an ADR number is effectively assigned when the ADR is merged, before being merged its number is provisional. Because multiple ADRs can be written at the same time, you end-up with multiple provisional ADRs with the same number, for example this is the third ADR-3:
|
||||
|
||||
1. ADR-3-clan-compat: see [#3212];
|
||||
2. ADR-3-fetching-nix-from-python: see [#3452];
|
||||
3. ADR-3-numbering-process: this ADR.
|
||||
|
||||
This situation makes it impossible to refer to an ADR by its number, and why I (@lopter) went with the arbitrary number 7 in [#3196].
|
||||
|
||||
We could solve this problem by using the PR number as the ADR number (@lassulus). The issue is that PR numbers are getting big in clan-core which does not make them easy to remember, or use in conversation and code (@lopter).
|
||||
|
||||
Another approach would be to move the ADRs in a different repository, this would reset the counter back to 1, and make it straightforward to keep ADR and PR numbers in sync (@lopter). The issue then is that ADR are not in context with their changes which makes them more difficult to review (@Mic92).
|
||||
|
||||
## Decision
|
||||
|
||||
A third approach would be to:
|
||||
|
||||
1. Commit ADRs before they are approved, so that the next ADR number gets assigned;
|
||||
1. Open a PR for the proposed ADR;
|
||||
1. Update the ADR file committed in step 1, so that its markdown contents point to the PR that tracks it.
|
||||
|
||||
## Consequences
|
||||
|
||||
### ADR have unique and memorable numbers trough their entire life cycle
|
||||
|
||||
This makes it easier to refer to them in conversation or in code.
|
||||
|
||||
### You need to have commit access to get an ADR number assigned
|
||||
|
||||
This makes it more difficult for someone external to the project to contribute an ADR.
|
||||
|
||||
### Creating a new ADR requires multiple commits
|
||||
|
||||
Maybe a script or CI flow could help with that if it becomes painful.
|
||||
|
||||
[#3212]: https://git.clan.lol/clan/clan-core/pulls/3212/
|
||||
[#3452]: https://git.clan.lol/clan/clan-core/pulls/3452/
|
||||
[#3196]: https://git.clan.lol/clan/clan-core/pulls/3196/
|
||||
@@ -58,7 +58,7 @@ nav:
|
||||
- Autoincludes: manual/adding-machines.md
|
||||
- Inventory:
|
||||
- Inventory: manual/inventory.md
|
||||
- Instances: manual/distributed-services.md
|
||||
- Services: manual/distributed-services.md
|
||||
- Secure Boot: manual/secure-boot.md
|
||||
- Flake-parts: manual/flake-parts.md
|
||||
- Authoring:
|
||||
@@ -71,14 +71,16 @@ nav:
|
||||
- Testing: contributing/testing.md
|
||||
- Repo Layout: manual/repo-layout.md
|
||||
- Migrate existing Flakes: manual/migration-guide.md
|
||||
- Migrate inventory Services: guides/migrate-inventory-services.md
|
||||
- Reference:
|
||||
- Overview: reference/index.md
|
||||
- Clan Modules:
|
||||
- Overview:
|
||||
- reference/clanModules/index.md
|
||||
- reference/clanModules/frontmatter/index.md
|
||||
# TODO: display the docs of the clan.service modules
|
||||
# - reference/clanServices/admin.md
|
||||
# This is the module overview and should stay at the top
|
||||
- reference/clanModules/admin.md
|
||||
- reference/clanModules/borgbackup-static.md
|
||||
- reference/clanModules/data-mesher.md
|
||||
- reference/clanModules/borgbackup.md
|
||||
|
||||
@@ -26,8 +26,7 @@ writeShellScriptBin "deploy-docs" ''
|
||||
trap "rm -rf $tmpdir" EXIT
|
||||
|
||||
if [ -n "''${SSH_HOMEPAGE_KEY-}" ]; then
|
||||
echo "$SSH_HOMEPAGE_KEY" > "$tmpdir/ssh_key"
|
||||
chmod 600 "$tmpdir/ssh_key"
|
||||
( umask 0177 && echo "$SSH_HOMEPAGE_KEY" > "$tmpdir/ssh_key" )
|
||||
sshExtraArgs="-i $tmpdir/ssh_key"
|
||||
else
|
||||
sshExtraArgs=
|
||||
|
||||
@@ -12,7 +12,7 @@ We discussed the initial architecture in [01-clan-service-modules](https://git.c
|
||||
|
||||
### A Minimal module
|
||||
|
||||
First of all we need to register our module into the `inventory.modules` attribute. Make sure to choose a unique name so the module doesn't have a name collision with any of the core modules.
|
||||
First of all we need to register our module into the `clan.modules` attribute. Make sure to choose a unique name so the module doesn't have a name collision with any of the core modules.
|
||||
|
||||
While not required we recommend to prefix your module attribute name.
|
||||
|
||||
@@ -22,20 +22,15 @@ i.e. `@hsjobeki/customNetworking`
|
||||
|
||||
```nix title="flake.nix"
|
||||
# ...
|
||||
|
||||
outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({
|
||||
outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } ({
|
||||
imports = [ inputs.clan-core.flakeModules.default ];
|
||||
# ...
|
||||
clan = {
|
||||
inventory = {
|
||||
# We could also inline the complete module spec here
|
||||
# For example
|
||||
# {...}: { _class = "clan.service"; ... };
|
||||
modules."@hsjobeki/customNetworking" = import ./service-modules/networking.nix;
|
||||
};
|
||||
|
||||
# If needed: Exporting the module for other people
|
||||
modules."@hsjobeki/customNetworking" = import ./service-modules/networking.nix;
|
||||
# We could also inline the complete module spec here
|
||||
# For example
|
||||
# {...}: { _class = "clan.service"; ... };
|
||||
};
|
||||
})
|
||||
```
|
||||
@@ -221,9 +216,6 @@ outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({self, lib, ...}:
|
||||
# ...
|
||||
clan = {
|
||||
# Register the module
|
||||
inventory.modules."@hsjobeki/messaging" = lib.importApply ./service-modules/messaging.nix { inherit self; };
|
||||
|
||||
# Expose the module for downstream users, 'self' would always point to this flake.
|
||||
modules."@hsjobeki/messaging" = lib.importApply ./service-modules/messaging.nix { inherit self; };
|
||||
};
|
||||
})
|
||||
@@ -250,7 +242,7 @@ outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({self, lib, ...}:
|
||||
# ...
|
||||
clan = {
|
||||
# Register the module
|
||||
inventory.modules."@hsjobeki/messaging" = {
|
||||
modules."@hsjobeki/messaging" = {
|
||||
# Create an option 'myClan' and assign it to 'self'
|
||||
options.myClan = lib.mkOption {
|
||||
default = self;
|
||||
|
||||
@@ -32,7 +32,7 @@ VM tests should be avoided wherever it is possible to implement a cheaper unit t
|
||||
|
||||
Existing nixos vm tests in clan-core can be found by using ripgrep:
|
||||
```shellSession
|
||||
rg "import.*/lib/test-base.nix"
|
||||
rg self.clanLib.test.baseTest
|
||||
```
|
||||
|
||||
### Locating definitions of failing VM tests
|
||||
@@ -50,7 +50,7 @@ example: locating the vm test named `borgbackup`:
|
||||
```shellSession
|
||||
$ rg "borgbackup =" ./checks
|
||||
./checks/flake-module.nix
|
||||
41: borgbackup = import ./borgbackup nixosTestArgs;
|
||||
44- wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||
```
|
||||
|
||||
-> the location of that test is `/checks/flake-module.nix` line `41`.
|
||||
@@ -99,15 +99,15 @@ Basically everything stated under the NixOS VM tests sections applies here, exce
|
||||
|
||||
Limitations:
|
||||
|
||||
- does not yet support networking
|
||||
- supports only one machine as of now
|
||||
|
||||
- Cannot run in interactive mode, however while the container test runs, it logs a nsenter command that can be used to log into each of the container.
|
||||
- setuid binaries don't work
|
||||
|
||||
### Where to find examples for NixOS container tests
|
||||
|
||||
Existing nixos container tests in clan-core can be found by using ripgrep:
|
||||
|
||||
```shellSession
|
||||
rg "import.*/lib/container-test.nix"
|
||||
rg self.clanLib.test.containerTest
|
||||
```
|
||||
|
||||
|
||||
|
||||
7
docs/site/guides/migrate-inventory-services.md
Normal file
7
docs/site/guides/migrate-inventory-services.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# How to migrate `Inventory.services`
|
||||
|
||||
## Further reference
|
||||
|
||||
- [Authoring a 'clan.service' module](../authoring/clanServices/index.md)
|
||||
- [Setting up `inventory.instances`](../manual/distributed-services.md)
|
||||
- [Inventory Reference](../reference/nix-api/inventory.md)
|
||||
@@ -1,8 +1,10 @@
|
||||
# Instances
|
||||
# Setting up `inventory.instances`
|
||||
|
||||
In Clan *distributed services* can be declaratively deployed using the `inventory.instances` attribute
|
||||
|
||||
First of all it might be needed to explain what we mean by the term *distributed service*
|
||||
|
||||
## What is considered a service?
|
||||
## What is considered a distributed service?
|
||||
|
||||
A **distributed service** is a system where multiple machines work together to provide a certain functionality, abstracting complexity and allowing for declarative configuration and management.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# :material-api: Overview
|
||||
|
||||
This section of the site provides an **automatically extracted** overview of the available options and commands within the Clan Framework.
|
||||
This section of the site provides an overview of available options and commands within the Clan Framework.
|
||||
|
||||
---
|
||||
|
||||
|
||||
56
flake.lock
generated
56
flake.lock
generated
@@ -16,17 +16,15 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745889637,
|
||||
"narHash": "sha256-+BW9rppchFYIiJldD+fZA3MS2OtPNrb8l27SC3GyoSk=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "11b5673d9c7290a6b96c2b6c6c5be600304f310f",
|
||||
"revCount": 415,
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/data-mesher"
|
||||
"lastModified": 1747008053,
|
||||
"narHash": "sha256-rob/qftmEuk+/JVGCIrOpv+LWjdmayFtebEKqRZXVAI=",
|
||||
"rev": "2666bb11f4287cfbdf3b7c5f55231c6b5772a436",
|
||||
"type": "tarball",
|
||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/2666bb11f4287cfbdf3b7c5f55231c6b5772a436.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/data-mesher"
|
||||
"type": "tarball",
|
||||
"url": "https://git.clan.lol/clan/data-mesher/archive/main.tar.gz"
|
||||
}
|
||||
},
|
||||
"disko": {
|
||||
@@ -36,11 +34,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745812220,
|
||||
"narHash": "sha256-hotBG0EJ9VmAHJYF0yhWuTVZpENHvwcJ2SxvIPrXm+g=",
|
||||
"lastModified": 1746729224,
|
||||
"narHash": "sha256-9R4sOLAK1w3Bq54H3XOJogdc7a6C2bLLmatOQ+5pf5w=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "d0c543d740fad42fe2c035b43c9d41127e073c78",
|
||||
"rev": "85555d27ded84604ad6657ecca255a03fd878607",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -76,11 +74,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745816321,
|
||||
"narHash": "sha256-Gyh/fkCDqVNGM0BWvk+4UAS17w2UI6iwnbQQCmc1TDI=",
|
||||
"lastModified": 1746708654,
|
||||
"narHash": "sha256-GeC99gu5H6+AjBXsn5dOhP4/ApuioGCBkufdmEIWPRs=",
|
||||
"owner": "nix-darwin",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "4515dacafb0ccd42e5395aacc49fd58a43027e01",
|
||||
"rev": "6cb36e8327421c61e5a3bbd08ed63491b616364a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -93,15 +91,13 @@
|
||||
"locked": {
|
||||
"lastModified": 1745005516,
|
||||
"narHash": "sha256-IVaoOGDIvAa/8I0sdiiZuKptDldrkDWUNf/+ezIRhyc=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "69d8bf596194c5c35a4e90dd02c52aa530caddf8",
|
||||
"revCount": 40,
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/nix-select"
|
||||
"type": "tarball",
|
||||
"url": "https://git.clan.lol/api/v1/repos/clan/nix-select/archive/69d8bf596194c5c35a4e90dd02c52aa530caddf8.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://git.clan.lol/clan/nix-select"
|
||||
"type": "tarball",
|
||||
"url": "https://git.clan.lol/clan/nix-select/archive/main.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixos-facter-modules": {
|
||||
@@ -122,10 +118,10 @@
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 315532800,
|
||||
"narHash": "sha256-+Elxpf3FLkgKfh81xrEjVolpJEn8+fKWqEJ3ZXbAbS4=",
|
||||
"rev": "29335f23bea5e34228349ea739f31ee79e267b88",
|
||||
"narHash": "sha256-kgy4FnRFGj62QO3kI6a6glFl8XUtKMylWGybnVCvycM=",
|
||||
"rev": "b3582c75c7f21ce0b429898980eddbbf05c68e55",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.05pre791229.29335f23bea5/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.05pre796313.b3582c75c7f2/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
@@ -153,11 +149,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745310711,
|
||||
"narHash": "sha256-ePyTpKEJTgX0gvgNQWd7tQYQ3glIkbqcW778RpHlqgA=",
|
||||
"lastModified": 1746485181,
|
||||
"narHash": "sha256-PxrrSFLaC7YuItShxmYbMgSuFFuwxBB+qsl9BZUnRvg=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "5e3e92b16d6fdf9923425a8d4df7496b2434f39c",
|
||||
"rev": "e93ee1d900ad264d65e9701a5c6f895683433386",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -188,11 +184,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745929750,
|
||||
"narHash": "sha256-k5ELLpTwRP/OElcLpNaFWLNf8GRDq4/eHBmFy06gGko=",
|
||||
"lastModified": 1746989248,
|
||||
"narHash": "sha256-uoQ21EWsAhyskNo8QxrTVZGjG/dV4x5NM1oSgrmNDJY=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "82bf32e541b30080d94e46af13d46da0708609ea",
|
||||
"rev": "708ec80ca82e2bbafa93402ccb66a35ff87900c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
nix-select.url = "git+https://git.clan.lol/clan/nix-select";
|
||||
nix-select.url = "https://git.clan.lol/clan/nix-select/archive/main.tar.gz";
|
||||
|
||||
data-mesher = {
|
||||
url = "git+https://git.clan.lol/clan/data-mesher";
|
||||
url = "https://git.clan.lol/clan/data-mesher/archive/main.tar.gz";
|
||||
inputs = {
|
||||
flake-parts.follows = "flake-parts";
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
@@ -40,7 +40,6 @@
|
||||
inputs@{
|
||||
flake-parts,
|
||||
nixpkgs,
|
||||
self,
|
||||
systems,
|
||||
...
|
||||
}:
|
||||
|
||||
@@ -37,7 +37,7 @@ let
|
||||
done
|
||||
if ! test -e ~/clan-core; then
|
||||
# git clone https://git.clan.lol/clan/clan-core.git ~/clan-core
|
||||
cp -rv ${self} clan-core
|
||||
cp -rv ${self.checks.x86_64-linux.clan-core-for-checks} clan-core
|
||||
fi
|
||||
cd clan-core
|
||||
clan machines morph demo-template --i-will-be-fired-for-using-this
|
||||
|
||||
@@ -10,6 +10,11 @@ let
|
||||
in
|
||||
{
|
||||
options = {
|
||||
_prefix = lib.mkOption {
|
||||
type = types.listOf types.str;
|
||||
internal = true;
|
||||
default = [ ];
|
||||
};
|
||||
self = lib.mkOption {
|
||||
type = types.raw;
|
||||
default = self;
|
||||
@@ -160,7 +165,6 @@ in
|
||||
# Those options are interfaced by the CLI
|
||||
# We don't specify the type here, for better performance.
|
||||
inventory = lib.mkOption { type = lib.types.raw; };
|
||||
inventoryValuesPrios = lib.mkOption { type = lib.types.raw; };
|
||||
# all exported clan templates from this clan
|
||||
templates = lib.mkOption { type = lib.types.raw; };
|
||||
# all exported clan modules from this clan
|
||||
@@ -169,6 +173,7 @@ in
|
||||
inventoryFile = lib.mkOption { type = lib.types.raw; };
|
||||
# The machine 'imports' generated by the inventory per machine
|
||||
inventoryClass = lib.mkOption { type = lib.types.raw; };
|
||||
evalServiceSchema = lib.mkOption { };
|
||||
# clan-core's modules
|
||||
clanModules = lib.mkOption { type = lib.types.raw; };
|
||||
source = lib.mkOption { type = lib.types.raw; };
|
||||
|
||||
@@ -44,6 +44,10 @@ let
|
||||
buildInventory {
|
||||
inherit inventory directory;
|
||||
flakeInputs = config.self.inputs;
|
||||
prefix = config._prefix ++ [ "inventoryClass" ];
|
||||
# TODO: remove inventory.modules, this is here for backwards compatibility
|
||||
localModuleSet =
|
||||
lib.filterAttrs (n: _: !inventory._legacyModules ? ${n}) inventory.modules // config.modules;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -177,6 +181,7 @@ in
|
||||
# Merge the meta attributes from the buildClan function
|
||||
{
|
||||
inventory.modules = clan-core.clanModules;
|
||||
inventory._legacyModules = clan-core.clanModules;
|
||||
}
|
||||
# config.inventory.meta <- config.meta
|
||||
{ inventory.meta = config.meta; }
|
||||
@@ -204,20 +209,21 @@ in
|
||||
|
||||
inherit inventoryClass;
|
||||
|
||||
# Endpoint that can be called to get a service schema
|
||||
evalServiceSchema = clan-core.clanLib.evalServiceSchema config.self;
|
||||
|
||||
# TODO: unify this interface
|
||||
# We should have only clan.modules. (consistent with clan.templates)
|
||||
inherit (clan-core) clanModules clanLib;
|
||||
modules = config.modules;
|
||||
|
||||
inherit inventoryFile;
|
||||
inventoryValuesPrios =
|
||||
# Temporary workaround
|
||||
builtins.removeAttrs (clan-core.clanLib.introspection.getPrios { options = inventory.options; })
|
||||
# tags are freeformType which is not supported yet.
|
||||
[ "tags" ];
|
||||
|
||||
templates = config.templates;
|
||||
inventory = config.inventory;
|
||||
# TODO: Remove this in about a month
|
||||
# It is only here for backwards compatibility for people with older CLI versions
|
||||
inventoryValuesPrios = inventoryClass.introspection;
|
||||
meta = config.inventory.meta;
|
||||
secrets = config.secrets;
|
||||
|
||||
|
||||
@@ -15,10 +15,27 @@ lib.fix (clanLib: {
|
||||
*/
|
||||
callLib = file: args: import file ({ inherit lib clanLib; } // args);
|
||||
|
||||
# ------------------------------------
|
||||
buildClan = clanLib.buildClanModule.buildClanWith {
|
||||
clan-core = self;
|
||||
inherit nixpkgs nix-darwin;
|
||||
};
|
||||
evalServiceSchema =
|
||||
self:
|
||||
{
|
||||
moduleSpec,
|
||||
flakeInputs ? self.inputs,
|
||||
localModuleSet ? self.clan.modules,
|
||||
}:
|
||||
let
|
||||
resolvedModule = clanLib.inventory.resolveModule {
|
||||
inherit moduleSpec flakeInputs localModuleSet;
|
||||
};
|
||||
in
|
||||
(clanLib.inventory.evalClanService {
|
||||
modules = [ resolvedModule ];
|
||||
prefix = [ ];
|
||||
}).config.result.api.schema;
|
||||
# ------------------------------------
|
||||
# ClanLib functions
|
||||
evalClan = clanLib.callLib ./inventory/eval-clan-modules { };
|
||||
|
||||
@@ -12,27 +12,38 @@ let
|
||||
inventory,
|
||||
directory,
|
||||
flakeInputs,
|
||||
prefix ? [ ],
|
||||
localModuleSet ? { },
|
||||
}:
|
||||
(lib.evalModules {
|
||||
# TODO: remove clanLib from specialArgs
|
||||
specialArgs = {
|
||||
inherit clanLib;
|
||||
};
|
||||
modules = [
|
||||
./builder
|
||||
(lib.modules.importApply ./service-list-from-inputs.nix {
|
||||
inherit flakeInputs clanLib localModuleSet;
|
||||
})
|
||||
{ inherit directory inventory; }
|
||||
(
|
||||
# config.distributedServices.allMachines.${name} or [ ];
|
||||
{ config, ... }:
|
||||
{
|
||||
|
||||
distributedServices = clanLib.inventory.mapInstances {
|
||||
inherit (config) inventory;
|
||||
inherit localModuleSet;
|
||||
inherit flakeInputs;
|
||||
prefix = prefix ++ [ "distributedServices" ];
|
||||
};
|
||||
machines = lib.mapAttrs (_machineName: v: {
|
||||
machineImports = v;
|
||||
}) config.distributedServices.allMachines;
|
||||
|
||||
}
|
||||
)
|
||||
(lib.modules.importApply ./inventory-introspection.nix { inherit clanLib; })
|
||||
];
|
||||
}).config;
|
||||
in
|
||||
|
||||
@@ -96,6 +96,12 @@ in
|
||||
./assertions.nix
|
||||
];
|
||||
options = {
|
||||
_legacyModules = lib.mkOption {
|
||||
internal = true;
|
||||
visible = false;
|
||||
default = { };
|
||||
};
|
||||
|
||||
options = lib.mkOption {
|
||||
internal = true;
|
||||
visible = false;
|
||||
@@ -138,6 +144,28 @@ in
|
||||
};
|
||||
```
|
||||
'';
|
||||
|
||||
apply =
|
||||
moduleSet:
|
||||
let
|
||||
allowedNames = lib.attrNames config._legacyModules;
|
||||
in
|
||||
if builtins.all (moduleName: builtins.elem moduleName allowedNames) (lib.attrNames moduleSet) then
|
||||
moduleSet
|
||||
else
|
||||
lib.warn ''
|
||||
`inventory.modules` will be deprecated soon.
|
||||
|
||||
Please migrate the following modules into `clan.service` modules
|
||||
and register them in `clan.modules`
|
||||
|
||||
${lib.concatStringsSep "\n" (
|
||||
map (m: "'${m}'") (lib.attrNames (lib.filterAttrs (n: _v: !builtins.elem n allowedNames) moduleSet))
|
||||
)}
|
||||
|
||||
See: https://docs.clan.lol/manual/distributed-services/
|
||||
And: https://docs.clan.lol/authoring/clanServices/
|
||||
'' moduleSet;
|
||||
};
|
||||
|
||||
assertions = lib.mkOption {
|
||||
|
||||
17
lib/inventory/build-inventory/inventory-introspection.nix
Normal file
17
lib/inventory/build-inventory/inventory-introspection.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{ clanLib }:
|
||||
{
|
||||
config,
|
||||
options,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options.introspection = lib.mkOption {
|
||||
readOnly = true;
|
||||
# TODO: use options.inventory instead of the evaluate config attribute
|
||||
default =
|
||||
builtins.removeAttrs (clanLib.introspection.getPrios { options = config.inventory.options; })
|
||||
# tags are freeformType which is not supported yet.
|
||||
[ "tags" ];
|
||||
};
|
||||
}
|
||||
43
lib/inventory/build-inventory/service-list-from-inputs.nix
Normal file
43
lib/inventory/build-inventory/service-list-from-inputs.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
flakeInputs,
|
||||
clanLib,
|
||||
localModuleSet,
|
||||
}:
|
||||
{ lib, config, ... }:
|
||||
|
||||
let
|
||||
|
||||
inspectModule =
|
||||
inputName: moduleName: module:
|
||||
let
|
||||
eval = clanLib.inventory.evalClanService {
|
||||
modules = [ module ];
|
||||
prefix = [
|
||||
inputName
|
||||
"clan"
|
||||
"modules"
|
||||
moduleName
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
manifest = eval.config.manifest;
|
||||
roles = lib.mapAttrs (_n: _v: { }) eval.config.roles;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.modulesPerSource = lib.mkOption {
|
||||
# { sourceName :: { moduleName :: {} }}
|
||||
default =
|
||||
let
|
||||
inputsWithModules = lib.filterAttrs (_inputName: v: v ? clan.modules) flakeInputs;
|
||||
|
||||
in
|
||||
lib.mapAttrs (
|
||||
inputName: v: lib.mapAttrs (inspectModule inputName) v.clan.modules
|
||||
) inputsWithModules;
|
||||
};
|
||||
options.localModules = lib.mkOption {
|
||||
default = lib.mapAttrs (inspectModule "self") localModuleSet;
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
resolvedRoles,
|
||||
instanceName,
|
||||
moduleName,
|
||||
allRoles,
|
||||
}:
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -11,7 +14,7 @@ let
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./interface.nix
|
||||
(lib.modules.importApply ./interface.nix { inherit allRoles; })
|
||||
# Role assertions
|
||||
{
|
||||
config.assertions = lib.foldlAttrs (
|
||||
@@ -24,7 +27,7 @@ in
|
||||
"${moduleName}.${instanceName}.roles.${roleName}.min" = {
|
||||
assertion = memberCount >= roleConstraints.min;
|
||||
message = ''
|
||||
The ${moduleName} module requires at least ${builtins.toString roleConstraints.min} members of the '${roleName}' role
|
||||
The '${moduleName}' module requires at least ${builtins.toString roleConstraints.min} members of the '${roleName}' role
|
||||
but found '${builtins.toString memberCount}' members within instance '${instanceName}':
|
||||
|
||||
${lib.concatLines members}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
allRoles,
|
||||
}:
|
||||
{
|
||||
lib,
|
||||
allRoles,
|
||||
moduleName,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -9,12 +10,6 @@ let
|
||||
rolesAttrs = builtins.groupBy lib.id allRoles;
|
||||
in
|
||||
{
|
||||
options.serviceName = mkOption {
|
||||
type = types.str;
|
||||
default = moduleName;
|
||||
readOnly = true;
|
||||
visible = false;
|
||||
};
|
||||
options.roles = lib.mapAttrs (
|
||||
_name: _:
|
||||
mkOption {
|
||||
|
||||
@@ -3,7 +3,7 @@ let
|
||||
services = clanLib.callLib ./distributed-service/inventory-adapter.nix { };
|
||||
in
|
||||
{
|
||||
inherit (services) evalClanService mapInstances;
|
||||
inherit (services) evalClanService mapInstances resolveModule;
|
||||
inherit (import ./build-inventory { inherit lib clanLib; }) buildInventory;
|
||||
interface = ./build-inventory/interface.nix;
|
||||
# Returns the list of machine names
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This module enables itself if
|
||||
# manifest.features.API = true
|
||||
# It converts the roles.interface to a json-schema
|
||||
{ clanLib, attrName }:
|
||||
{ clanLib, prefix }:
|
||||
let
|
||||
converter = clanLib.jsonschema {
|
||||
includeDefaults = true;
|
||||
@@ -45,7 +45,7 @@ in
|
||||
|
||||
To see the evaluation problem run
|
||||
|
||||
nix eval .#clanInternals.inventoryClass.distributedServices.importedModulesEvaluated.${attrName}.config.result.api.schema.${roleName}
|
||||
nix eval .#${lib.concatStringsSep "." prefix}.config.result.api.schema.${roleName}
|
||||
'';
|
||||
assertion = (builtins.tryEval (lib.deepSeq config.result.api.schema.${roleName} true)).success;
|
||||
};
|
||||
|
||||
@@ -16,27 +16,73 @@
|
||||
}:
|
||||
let
|
||||
evalClanService =
|
||||
{ modules, key }:
|
||||
{ modules, prefix }:
|
||||
(lib.evalModules {
|
||||
class = "clan.service";
|
||||
modules = [
|
||||
./service-module.nix
|
||||
# feature modules
|
||||
(lib.modules.importApply ./api-feature.nix {
|
||||
inherit clanLib;
|
||||
attrName = key;
|
||||
inherit clanLib prefix;
|
||||
})
|
||||
] ++ modules;
|
||||
});
|
||||
|
||||
resolveModule =
|
||||
{
|
||||
moduleSpec,
|
||||
flakeInputs,
|
||||
localModuleSet,
|
||||
}:
|
||||
let
|
||||
# TODO:
|
||||
resolvedModuleSet =
|
||||
# If the module.name is self then take the modules defined in the flake
|
||||
# Otherwise its an external input which provides the modules via 'clan.modules' attribute
|
||||
if moduleSpec.input == null then
|
||||
localModuleSet
|
||||
else
|
||||
let
|
||||
input =
|
||||
flakeInputs.${moduleSpec.input} or (throw ''
|
||||
Flake doesn't provide input with name '${moduleSpec.input}'
|
||||
|
||||
Choose one of the following inputs:
|
||||
- ${
|
||||
builtins.concatStringsSep "\n- " (
|
||||
lib.attrNames (lib.filterAttrs (_name: input: input ? clan) flakeInputs)
|
||||
)
|
||||
}
|
||||
|
||||
To import a local module from 'clan.modules' remove the 'input' attribute from the module definition
|
||||
Remove the following line from the module definition:
|
||||
|
||||
...
|
||||
- module.input = "${moduleSpec.input}"
|
||||
|
||||
'');
|
||||
clanAttrs =
|
||||
input.clan
|
||||
or (throw "It seems the flake input ${moduleSpec.input} doesn't export any clan resources");
|
||||
in
|
||||
clanAttrs.modules;
|
||||
|
||||
resolvedModule =
|
||||
resolvedModuleSet.${moduleSpec.name}
|
||||
or (throw "flake doesn't provide clan-module with name ${moduleSpec.name}");
|
||||
in
|
||||
resolvedModule;
|
||||
in
|
||||
{
|
||||
inherit evalClanService;
|
||||
inherit evalClanService resolveModule;
|
||||
mapInstances =
|
||||
{
|
||||
# This is used to resolve the module imports from 'flake.inputs'
|
||||
flakeInputs,
|
||||
# The clan inventory
|
||||
inventory,
|
||||
localModuleSet,
|
||||
prefix ? [ ],
|
||||
}:
|
||||
let
|
||||
# machineHasTag = machineName: tagName: lib.elem tagName inventory.machines.${machineName}.tags;
|
||||
@@ -45,42 +91,11 @@ in
|
||||
importedModuleWithInstances = lib.mapAttrs (
|
||||
instanceName: instance:
|
||||
let
|
||||
# TODO:
|
||||
resolvedModuleSet =
|
||||
# If the module.name is self then take the modules defined in the flake
|
||||
# Otherwise its an external input which provides the modules via 'clan.modules' attribute
|
||||
if instance.module.input == null then
|
||||
inventory.modules
|
||||
else
|
||||
let
|
||||
input =
|
||||
flakeInputs.${instance.module.input} or (throw ''
|
||||
Flake doesn't provide input with name '${instance.module.input}'
|
||||
|
||||
Choose one of the following inputs:
|
||||
- ${
|
||||
builtins.concatStringsSep "\n- " (
|
||||
lib.attrNames (lib.filterAttrs (_name: input: input ? clan) flakeInputs)
|
||||
)
|
||||
}
|
||||
|
||||
To import a local module from 'inventory.modules' remove the 'input' attribute from the module definition
|
||||
Remove the following line from the module definition:
|
||||
|
||||
...
|
||||
- module.input = "${instance.module.input}"
|
||||
|
||||
|
||||
'');
|
||||
clanAttrs =
|
||||
input.clan
|
||||
or (throw "It seems the flake input ${instance.module.input} doesn't export any clan resources");
|
||||
in
|
||||
clanAttrs.modules;
|
||||
|
||||
resolvedModule =
|
||||
resolvedModuleSet.${instance.module.name}
|
||||
or (throw "flake doesn't provide clan-module with name ${instance.module.name}");
|
||||
resolvedModule = resolveModule {
|
||||
moduleSpec = instance.module;
|
||||
inherit localModuleSet;
|
||||
inherit flakeInputs;
|
||||
};
|
||||
|
||||
# Every instance includes machines via roles
|
||||
# :: { client :: ... }
|
||||
@@ -138,7 +153,7 @@ in
|
||||
importedModulesEvaluated = lib.mapAttrs (
|
||||
module_ident: instances:
|
||||
evalClanService {
|
||||
key = module_ident;
|
||||
prefix = prefix ++ [ module_ident ];
|
||||
modules =
|
||||
[
|
||||
# Import the resolved module.
|
||||
|
||||
78
lib/inventory/distributed-service/manifest/default.nix
Normal file
78
lib/inventory/distributed-service/manifest/default.nix
Normal file
@@ -0,0 +1,78 @@
|
||||
{ lib, config, ... }:
|
||||
let
|
||||
inherit (lib) mkOption;
|
||||
inherit (lib) types;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
name = mkOption {
|
||||
description = ''
|
||||
The name of the module
|
||||
|
||||
Mainly used to create an error context while evaluating.
|
||||
This helps backtracking which module was included; And where an error came from originally.
|
||||
'';
|
||||
type = types.str;
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
A Short description of the module.
|
||||
'';
|
||||
defaultText = "Short description";
|
||||
default = config.name;
|
||||
};
|
||||
categories = mkOption {
|
||||
default = [ "Uncategorized" ];
|
||||
description = ''
|
||||
Categories are used for Grouping and searching.
|
||||
|
||||
While initial oriented on [freedesktop](https://specifications.freedesktop.org/menu-spec/latest/category-registry.html) the following categories are allowed
|
||||
'';
|
||||
type = types.listOf (
|
||||
types.enum [
|
||||
"AudioVideo"
|
||||
"Audio"
|
||||
"Video"
|
||||
"Development"
|
||||
"Education"
|
||||
"Game"
|
||||
"Graphics"
|
||||
"Social"
|
||||
"Network"
|
||||
"Office"
|
||||
"Science"
|
||||
"System"
|
||||
"Settings"
|
||||
"Utility"
|
||||
"Uncategorized"
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
features = mkOption {
|
||||
description = ''
|
||||
Enable built-in features for the module
|
||||
|
||||
See the documentation for each feature:
|
||||
- API
|
||||
'';
|
||||
type = types.submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
options.API = mkOption {
|
||||
type = types.bool;
|
||||
# This is read only, because we don't support turning it off yet
|
||||
readOnly = true;
|
||||
default = true;
|
||||
description = ''
|
||||
Enables automatic API schema conversion for the interface of this module.
|
||||
'';
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -232,41 +232,7 @@ in
|
||||
description = "Meta information about this module itself";
|
||||
type = submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
options = {
|
||||
name = mkOption {
|
||||
description = ''
|
||||
The name of the module
|
||||
|
||||
Mainly used to create an error context while evaluating.
|
||||
This helps backtracking which module was included; And where an error came from originally.
|
||||
'';
|
||||
type = types.str;
|
||||
};
|
||||
features = mkOption {
|
||||
description = ''
|
||||
Enable built-in features for the module
|
||||
|
||||
See the documentation for each feature:
|
||||
- API
|
||||
'';
|
||||
type = types.submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
options.API = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enables automatic API schema conversion for the interface of this module.
|
||||
'';
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
}
|
||||
./manifest/default.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -41,9 +41,13 @@ let
|
||||
|
||||
callInventoryAdapter =
|
||||
inventoryModule:
|
||||
let
|
||||
inventory = evalInventory inventoryModule;
|
||||
in
|
||||
clanLib.inventory.mapInstances {
|
||||
flakeInputs = flakeInputsFixture;
|
||||
inventory = evalInventory inventoryModule;
|
||||
inherit inventory;
|
||||
localModuleSet = inventory.modules;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
@@ -34,37 +34,60 @@ let
|
||||
allModules,
|
||||
}:
|
||||
lib.evalModules {
|
||||
specialArgs = {
|
||||
inherit moduleName resolvedRoles instanceName;
|
||||
allRoles = getRoles "inventory.modules" allModules moduleName;
|
||||
};
|
||||
modules = [
|
||||
(getFrontmatter allModules.${moduleName} moduleName)
|
||||
./interface.nix
|
||||
{
|
||||
constraints.imports = [
|
||||
(lib.modules.importApply ../constraints {
|
||||
inherit moduleName resolvedRoles instanceName;
|
||||
allRoles = getRoles "inventory.modules" allModules moduleName;
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# For Documentation purposes only
|
||||
frontmatterOptions =
|
||||
(lib.evalModules {
|
||||
specialArgs = {
|
||||
moduleName = "{moduleName}";
|
||||
allRoles = [ "{roleName}" ];
|
||||
};
|
||||
modules = [
|
||||
./interface.nix
|
||||
{
|
||||
constraints.imports = [
|
||||
(lib.modules.importApply ../constraints {
|
||||
moduleName = "{moduleName}";
|
||||
allRoles = [ "{roleName}" ];
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
}).options;
|
||||
|
||||
migratedModules = [ "admin" ];
|
||||
|
||||
makeModuleNotFoundError =
|
||||
serviceName:
|
||||
if builtins.elem serviceName migratedModules then
|
||||
''
|
||||
(Legacy) ClanModule not found: '${serviceName}'.
|
||||
|
||||
Please update your configuration to use this module via 'inventory.instances'
|
||||
See: https://docs.clan.lol/manual/distributed-services/
|
||||
''
|
||||
else
|
||||
''
|
||||
(Legacy) ClanModule not found: '${serviceName}'.
|
||||
|
||||
Make sure the module is added to inventory.modules.${serviceName}
|
||||
'';
|
||||
# This is a legacy function
|
||||
# Old modules needed to define their roles by directory
|
||||
# This means if this function gets anything other than a string/path it will throw
|
||||
getRoles =
|
||||
scope: allModules: serviceName:
|
||||
_scope: allModules: serviceName:
|
||||
let
|
||||
module =
|
||||
allModules.${serviceName}
|
||||
or (throw "(Legacy) ClanModule not found: '${serviceName}'. Make sure the module is added to ${scope}");
|
||||
module = allModules.${serviceName} or (throw (makeModuleNotFoundError serviceName));
|
||||
moduleType = (lib.typeOf module);
|
||||
checked =
|
||||
if
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
lib,
|
||||
specialArgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -76,9 +75,8 @@ in
|
||||
```
|
||||
'';
|
||||
type = types.submoduleWith {
|
||||
inherit specialArgs;
|
||||
modules = [
|
||||
../constraints
|
||||
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ in
|
||||
lib.lazyDerivation {
|
||||
# lazyDerivation improves performance when only passthru items and/or meta are used.
|
||||
derivation = hostPkgs.stdenv.mkDerivation {
|
||||
name = "vm-test-run-${config.name}";
|
||||
name = "container-test-run-${config.name}";
|
||||
|
||||
requiredSystemFeatures = [ "uid-range" ];
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ import argparse
|
||||
import ctypes
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import types
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
from contextlib import _GeneratorContextManager
|
||||
from dataclasses import dataclass
|
||||
@@ -13,6 +15,8 @@ from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Any
|
||||
|
||||
from colorama import Fore, Style
|
||||
|
||||
from .logger import AbstractLogger, CompositeLogger, TerminalLogger
|
||||
|
||||
# Load the C library
|
||||
@@ -187,6 +191,22 @@ class Machine:
|
||||
if line_pattern.match(line)
|
||||
)
|
||||
|
||||
def nsenter_command(self, command: str) -> list[str]:
|
||||
return [
|
||||
"nsenter",
|
||||
"--target",
|
||||
str(self.container_pid),
|
||||
"--mount",
|
||||
"--uts",
|
||||
"--ipc",
|
||||
"--net",
|
||||
"--pid",
|
||||
"--cgroup",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
command,
|
||||
]
|
||||
|
||||
def execute(
|
||||
self,
|
||||
command: str,
|
||||
@@ -228,23 +248,10 @@ class Machine:
|
||||
"""
|
||||
|
||||
# Always run command with shell opts
|
||||
command = f"set -eo pipefail; source /etc/profile; set -u; {command}"
|
||||
command = f"set -eo pipefail; source /etc/profile; set -xu; {command}"
|
||||
|
||||
proc = subprocess.run(
|
||||
[
|
||||
"nsenter",
|
||||
"--target",
|
||||
str(self.container_pid),
|
||||
"--mount",
|
||||
"--uts",
|
||||
"--ipc",
|
||||
"--net",
|
||||
"--pid",
|
||||
"--cgroup",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
command,
|
||||
],
|
||||
self.nsenter_command(command),
|
||||
timeout=timeout,
|
||||
check=False,
|
||||
stdout=subprocess.PIPE,
|
||||
@@ -465,6 +472,43 @@ class Driver:
|
||||
print(f"Starting {machine.name}")
|
||||
machine.start()
|
||||
|
||||
# Print copy-pastable nsenter command to debug container tests
|
||||
for machine in self.machines:
|
||||
nspawn_uuid = uuid.uuid4()
|
||||
|
||||
# We lauch a sleep here, so we can pgrep the process cmdline for
|
||||
# the uuid
|
||||
sleep = shutil.which("sleep")
|
||||
assert sleep is not None, "sleep command not found"
|
||||
machine.execute(
|
||||
f"systemd-run /bin/sh -c '{sleep} 999999999 && echo {nspawn_uuid}'",
|
||||
)
|
||||
|
||||
print(f"nsenter for {machine.name}:")
|
||||
print(
|
||||
" ".join(
|
||||
[
|
||||
Style.BRIGHT,
|
||||
Fore.CYAN,
|
||||
"sudo",
|
||||
"nsenter",
|
||||
"--user",
|
||||
"--target",
|
||||
f"$(\\pgrep -f '^/bin/sh.*{nspawn_uuid}')",
|
||||
"--mount",
|
||||
"--uts",
|
||||
"--ipc",
|
||||
"--net",
|
||||
"--pid",
|
||||
"--cgroup",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"bash",
|
||||
Style.RESET_ALL,
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
def test_symbols(self) -> dict[str, Any]:
|
||||
general_symbols = {
|
||||
"start_all": self.start_all,
|
||||
|
||||
@@ -22,6 +22,9 @@ in
|
||||
pkgs,
|
||||
self,
|
||||
useContainers ? true,
|
||||
# Displayed for better error messages, otherwise the placeholder
|
||||
system ? "<system>",
|
||||
attrName ? "<check_name>",
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -35,7 +38,7 @@ in
|
||||
{
|
||||
imports = [
|
||||
nixosTest
|
||||
] ++ lib.optionals (useContainers) [ ./container-test-driver/driver-module.nix ];
|
||||
] ++ lib.optionals useContainers [ ./container-test-driver/driver-module.nix ];
|
||||
options = {
|
||||
clanSettings = mkOption {
|
||||
default = { };
|
||||
@@ -60,6 +63,15 @@ in
|
||||
};
|
||||
modules = [
|
||||
clanLib.buildClanModule.flakePartsModule
|
||||
{
|
||||
_prefix = [
|
||||
"checks"
|
||||
system
|
||||
attrName
|
||||
"config"
|
||||
"clan"
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
21
module.nix
Normal file
21
module.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "test";
|
||||
|
||||
roles.peer.interface =
|
||||
{ ... }:
|
||||
{
|
||||
options.debug = lib.mkOption { default = 1; };
|
||||
};
|
||||
|
||||
roles.peer.perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule = {
|
||||
options.debug = lib.mkOption {
|
||||
default = settings;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -39,9 +39,44 @@ in
|
||||
type = submodule { imports = [ ./interface.nix ]; };
|
||||
};
|
||||
|
||||
config.system.clan.deployment.data = {
|
||||
vars = config.clan.core.vars._serialized;
|
||||
inherit (config.clan.core.networking) targetHost buildHost;
|
||||
inherit (config.clan.core.deployment) requireExplicitUpdate;
|
||||
config = {
|
||||
# check all that all non-secret files have no owner/group/mode set
|
||||
warnings = lib.foldl' (
|
||||
warnings: generator:
|
||||
warnings
|
||||
++ lib.foldl' (
|
||||
warnings: file:
|
||||
warnings
|
||||
++
|
||||
lib.optional
|
||||
(
|
||||
!file.secret
|
||||
&& (
|
||||
file.owner != "root"
|
||||
|| file.group != (if _class == "darwin" then "wheel" else "root")
|
||||
|| file.mode != "0400"
|
||||
)
|
||||
)
|
||||
''
|
||||
The config.clan.core.vars.generators.${generator.name}.files.${file.name} is not secret:
|
||||
${lib.optionalString (file.owner != "root") ''
|
||||
The owner is set to ${file.owner}, but should be root.
|
||||
''}
|
||||
${lib.optionalString (file.group != (if _class == "darwin" then "wheel" else "root")) ''
|
||||
The group is set to ${file.group}, but should be ${if _class == "darwin" then "wheel" else "root"}.
|
||||
''}
|
||||
${lib.optionalString (file.mode != "0400") ''
|
||||
The mode is set to ${file.mode}, but should be 0400.
|
||||
''}
|
||||
This doesn't work because the file will be added to the nix store
|
||||
''
|
||||
) [ ] (lib.attrValues generator.files)
|
||||
) [ ] (lib.attrValues config.clan.core.vars.generators);
|
||||
|
||||
system.clan.deployment.data = {
|
||||
vars = config.clan.core.vars._serialized;
|
||||
inherit (config.clan.core.networking) targetHost buildHost;
|
||||
inherit (config.clan.core.deployment) requireExplicitUpdate;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ in
|
||||
internal = true;
|
||||
description = ''
|
||||
JSON serialization of the generators.
|
||||
This is read from the python client to generate the specified ressources.
|
||||
This is read from the python client to generate the specified resources.
|
||||
'';
|
||||
default = {
|
||||
# TODO: We don't support per-machine choice of backends
|
||||
@@ -241,12 +241,30 @@ in
|
||||
type = bool;
|
||||
default = true;
|
||||
};
|
||||
flakePath = lib.mkOption {
|
||||
description = ''
|
||||
The path to the file containing the content of the generated value.
|
||||
This will be set automatically
|
||||
'';
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
path = lib.mkOption {
|
||||
description = ''
|
||||
The path to the file containing the content of the generated value.
|
||||
This will be set automatically
|
||||
'';
|
||||
type = str;
|
||||
defaultText = ''
|
||||
builtins.path {
|
||||
name = "$${generator.config._module.args.name}_$${file.config._module.args.name}";
|
||||
path = file.config.flakePath;
|
||||
}
|
||||
'';
|
||||
default = builtins.path {
|
||||
name = "${generator.config._module.args.name}_${file.config._module.args.name}";
|
||||
path = file.config.flakePath;
|
||||
};
|
||||
};
|
||||
neededFor = lib.mkOption {
|
||||
description = ''
|
||||
|
||||
@@ -11,7 +11,7 @@ in
|
||||
config.clan.core.vars.settings = mkIf (config.clan.core.vars.settings.publicStore == "in_repo") {
|
||||
publicModule = "clan_cli.vars.public_modules.in_repo";
|
||||
fileModule = file: {
|
||||
path = mkIf (file.config.secret == false) (
|
||||
flakePath = mkIf (file.config.secret == false) (
|
||||
if file.config.share then
|
||||
(
|
||||
config.clan.core.settings.directory
|
||||
@@ -25,9 +25,9 @@ in
|
||||
);
|
||||
value = mkIf (file.config.secret == false) (
|
||||
# dynamically adjust priority to allow overriding with mkDefault in case the file is not found
|
||||
if (pathExists file.config.path) then
|
||||
if (pathExists file.config.flakePath) then
|
||||
# if the file is found it should have normal priority
|
||||
readFile file.config.path
|
||||
readFile file.config.flakePath
|
||||
else
|
||||
# if the file is not found, we want to downgrade the priority, to allow overriding via mkDefault
|
||||
mkOptionDefault (
|
||||
|
||||
@@ -18,7 +18,7 @@ let
|
||||
config.clan.core.settings.directory
|
||||
+ "/vars/per-machine/${machineName}/${secret.generator}/${secret.name}/secret";
|
||||
|
||||
vars = collectFiles config.clan.core.vars;
|
||||
vars = collectFiles config.clan.core.vars.generators;
|
||||
in
|
||||
{
|
||||
config.clan.core.vars.settings = lib.mkIf (config.clan.core.vars.settings.secretStore == "sops") {
|
||||
@@ -49,7 +49,10 @@ in
|
||||
mode
|
||||
neededForUsers
|
||||
;
|
||||
sopsFile = secretPath secret;
|
||||
sopsFile = builtins.path {
|
||||
name = "${secret.generator}_${secret.name}";
|
||||
path = secretPath secret;
|
||||
};
|
||||
format = "binary";
|
||||
};
|
||||
}) (builtins.filter (x: builtins.pathExists (secretPath x)) vars)
|
||||
|
||||
@@ -13,7 +13,7 @@ in
|
||||
{
|
||||
|
||||
collectFiles =
|
||||
vars:
|
||||
generators:
|
||||
let
|
||||
relevantFiles =
|
||||
generator:
|
||||
@@ -30,7 +30,7 @@ in
|
||||
inherit (generator) share;
|
||||
inherit (file) owner group mode;
|
||||
}) (relevantFiles generator)
|
||||
) vars.generators
|
||||
) generators
|
||||
);
|
||||
in
|
||||
allFiles;
|
||||
|
||||
@@ -29,6 +29,8 @@ mkShell {
|
||||
export GIT_ROOT=$(git rev-parse --show-toplevel)
|
||||
export PKG_ROOT=$GIT_ROOT/pkgs/clan-app
|
||||
|
||||
export CLAN_CORE_PATH="$GIT_ROOT"
|
||||
|
||||
# Add current package to PYTHONPATH
|
||||
export PYTHONPATH="$PKG_ROOT''${PYTHONPATH:+:$PYTHONPATH:}"
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ from pathlib import Path
|
||||
from types import ModuleType
|
||||
|
||||
# These imports are unused, but necessary for @API.register to run once.
|
||||
from clan_lib.api import admin, directory, disk, iwd, mdns_discovery, modules
|
||||
from clan_lib.api import directory, disk, iwd, mdns_discovery, modules
|
||||
|
||||
from .arg_actions import AppendOptionAction
|
||||
from .clan import show, update
|
||||
|
||||
# API endpoints that are not used in the cli.
|
||||
__all__ = ["admin", "directory", "disk", "iwd", "mdns_discovery", "modules", "update"]
|
||||
__all__ = ["directory", "disk", "iwd", "mdns_discovery", "modules", "update"]
|
||||
|
||||
from . import (
|
||||
backups,
|
||||
|
||||
@@ -19,21 +19,23 @@ def create_backup(machine: Machine, provider: str | None = None) -> None:
|
||||
if not backup_scripts["providers"]:
|
||||
msg = "No providers specified"
|
||||
raise ClanError(msg)
|
||||
for provider in backup_scripts["providers"]:
|
||||
proc = machine.target_host.run(
|
||||
[backup_scripts["providers"][provider]["create"]],
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
msg = "failed to start backup"
|
||||
raise ClanError(msg)
|
||||
print("successfully started backup")
|
||||
with machine.target_host() as host:
|
||||
for provider in backup_scripts["providers"]:
|
||||
proc = host.run(
|
||||
[backup_scripts["providers"][provider]["create"]],
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
msg = "failed to start backup"
|
||||
raise ClanError(msg)
|
||||
print("successfully started backup")
|
||||
else:
|
||||
if provider not in backup_scripts["providers"]:
|
||||
msg = f"provider {provider} not found"
|
||||
raise ClanError(msg)
|
||||
proc = machine.target_host.run(
|
||||
[backup_scripts["providers"][provider]["create"]],
|
||||
)
|
||||
with machine.target_host() as host:
|
||||
proc = host.run(
|
||||
[backup_scripts["providers"][provider]["create"]],
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
msg = "failed to start backup"
|
||||
raise ClanError(msg)
|
||||
|
||||
@@ -10,6 +10,7 @@ from clan_cli.completions import (
|
||||
)
|
||||
from clan_cli.errors import ClanError
|
||||
from clan_cli.machines.machines import Machine
|
||||
from clan_cli.ssh.host import Host
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -18,11 +19,11 @@ class Backup:
|
||||
job_name: str | None = None
|
||||
|
||||
|
||||
def list_provider(machine: Machine, provider: str) -> list[Backup]:
|
||||
def list_provider(machine: Machine, host: Host, provider: str) -> list[Backup]:
|
||||
results = []
|
||||
backup_metadata = machine.eval_nix("config.clan.core.backups")
|
||||
list_command = backup_metadata["providers"][provider]["list"]
|
||||
proc = machine.target_host.run(
|
||||
proc = host.run(
|
||||
[list_command],
|
||||
RunOpts(log=Log.NONE, check=False),
|
||||
)
|
||||
@@ -48,12 +49,13 @@ def list_provider(machine: Machine, provider: str) -> list[Backup]:
|
||||
def list_backups(machine: Machine, provider: str | None = None) -> list[Backup]:
|
||||
backup_metadata = machine.eval_nix("config.clan.core.backups")
|
||||
results = []
|
||||
if provider is None:
|
||||
for _provider in backup_metadata["providers"]:
|
||||
results += list_provider(machine, _provider)
|
||||
with machine.target_host() as host:
|
||||
if provider is None:
|
||||
for _provider in backup_metadata["providers"]:
|
||||
results += list_provider(machine, host, _provider)
|
||||
|
||||
else:
|
||||
results += list_provider(machine, provider)
|
||||
else:
|
||||
results += list_provider(machine, host, provider)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -8,9 +8,12 @@ from clan_cli.completions import (
|
||||
)
|
||||
from clan_cli.errors import ClanError
|
||||
from clan_cli.machines.machines import Machine
|
||||
from clan_cli.ssh.host import Host
|
||||
|
||||
|
||||
def restore_service(machine: Machine, name: str, provider: str, service: str) -> None:
|
||||
def restore_service(
|
||||
machine: Machine, host: Host, name: str, provider: str, service: str
|
||||
) -> None:
|
||||
backup_metadata = machine.eval_nix("config.clan.core.backups")
|
||||
backup_folders = machine.eval_nix("config.clan.core.state")
|
||||
|
||||
@@ -25,7 +28,7 @@ def restore_service(machine: Machine, name: str, provider: str, service: str) ->
|
||||
env["FOLDERS"] = ":".join(set(folders))
|
||||
|
||||
if pre_restore := backup_folders[service]["preRestoreCommand"]:
|
||||
proc = machine.target_host.run(
|
||||
proc = host.run(
|
||||
[pre_restore],
|
||||
RunOpts(log=Log.STDERR),
|
||||
extra_env=env,
|
||||
@@ -34,7 +37,7 @@ def restore_service(machine: Machine, name: str, provider: str, service: str) ->
|
||||
msg = f"failed to run preRestoreCommand: {pre_restore}, error was: {proc.stdout}"
|
||||
raise ClanError(msg)
|
||||
|
||||
proc = machine.target_host.run(
|
||||
proc = host.run(
|
||||
[backup_metadata["providers"][provider]["restore"]],
|
||||
RunOpts(log=Log.STDERR),
|
||||
extra_env=env,
|
||||
@@ -44,7 +47,7 @@ def restore_service(machine: Machine, name: str, provider: str, service: str) ->
|
||||
raise ClanError(msg)
|
||||
|
||||
if post_restore := backup_folders[service]["postRestoreCommand"]:
|
||||
proc = machine.target_host.run(
|
||||
proc = host.run(
|
||||
[post_restore],
|
||||
RunOpts(log=Log.STDERR),
|
||||
extra_env=env,
|
||||
@@ -61,18 +64,19 @@ def restore_backup(
|
||||
service: str | None = None,
|
||||
) -> None:
|
||||
errors = []
|
||||
if service is None:
|
||||
backup_folders = machine.eval_nix("config.clan.core.state")
|
||||
for _service in backup_folders:
|
||||
with machine.target_host() as host:
|
||||
if service is None:
|
||||
backup_folders = machine.eval_nix("config.clan.core.state")
|
||||
for _service in backup_folders:
|
||||
try:
|
||||
restore_service(machine, host, name, provider, _service)
|
||||
except ClanError as e:
|
||||
errors.append(f"{_service}: {e}")
|
||||
else:
|
||||
try:
|
||||
restore_service(machine, name, provider, _service)
|
||||
restore_service(machine, host, name, provider, service)
|
||||
except ClanError as e:
|
||||
errors.append(f"{_service}: {e}")
|
||||
else:
|
||||
try:
|
||||
restore_service(machine, name, provider, service)
|
||||
except ClanError as e:
|
||||
errors.append(f"{service}: {e}")
|
||||
errors.append(f"{service}: {e}")
|
||||
if errors:
|
||||
raise ClanError(
|
||||
"Restore failed for the following services:\n" + "\n".join(errors)
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from clan_cli.cmd import run
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from clan_cli.cmd import Log, RunOpts, run
|
||||
from clan_cli.nix import nix_shell
|
||||
|
||||
_works: bool | None = None
|
||||
@@ -12,6 +16,11 @@ def bubblewrap_works() -> bool:
|
||||
|
||||
|
||||
def _bubblewrap_works() -> bool:
|
||||
real_bash_path = Path("bash")
|
||||
if os.environ.get("IN_NIX_SANDBOX"):
|
||||
bash_executable_path = Path(str(shutil.which("bash")))
|
||||
real_bash_path = bash_executable_path.resolve()
|
||||
|
||||
# fmt: off
|
||||
cmd = nix_shell(
|
||||
[
|
||||
@@ -30,13 +39,10 @@ def _bubblewrap_works() -> bool:
|
||||
"--gid", "1000",
|
||||
"--",
|
||||
# do nothing, just test if bash executes
|
||||
"bash", "-c", ":"
|
||||
str(real_bash_path), "-c", ":"
|
||||
],
|
||||
)
|
||||
|
||||
# fmt: on
|
||||
try:
|
||||
run(cmd)
|
||||
except Exception:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
res = run(cmd, RunOpts(log=Log.BOTH, check=False))
|
||||
return res.returncode == 0
|
||||
|
||||
@@ -107,7 +107,7 @@ def create_clan(opts: CreateOptions) -> CreateClanResponse:
|
||||
response.flake_update = flake_update
|
||||
|
||||
if opts.initial:
|
||||
init_inventory(str(opts.dest), init=opts.initial)
|
||||
init_inventory(Flake(str(opts.dest)), init=opts.initial)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user