Compare commits
2 Commits
feat/termi
...
options-wa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41635dd350 | ||
|
|
16d16faa9c |
@@ -1,9 +1,12 @@
|
||||
name: checks
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
checks-impure:
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- run: nix run .#impure-checks
|
||||
|
||||
@@ -7,7 +7,7 @@ jobs:
|
||||
deploy-docs:
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- run: nix run .#deploy-docs
|
||||
env:
|
||||
SSH_HOMEPAGE_KEY: ${{ secrets.SSH_HOMEPAGE_KEY }}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
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
|
||||
env:
|
||||
CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
|
||||
run: |
|
||||
export GIT_AUTHOR_NAME=clan-bot GIT_AUTHOR_EMAIL=clan-bot@clan.lol GIT_COMMITTER_NAME=clan-bot GIT_COMMITTER_EMAIL=clan-bot@clan.lol
|
||||
git commit -am "Update pinned clan-core for checks"
|
||||
git push origin +HEAD:update-clan-core-for-checks
|
||||
set -x
|
||||
resp=$(nix run --inputs-from . nixpkgs#curl -- -X POST \
|
||||
-H "Authorization: token $CI_BOT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"head": "update-clan-core-for-checks",
|
||||
"base": "main",
|
||||
"title": "Update Clan Core for Checks",
|
||||
"body": "This PR updates the pinned clan-core flake input that is used for checks."
|
||||
}' \
|
||||
"https://git.clan.lol/api/v1/repos/clan/clan-core/pulls")
|
||||
pr_number=$(echo "$resp" | jq -r '.number')
|
||||
|
||||
# Merge when succeed
|
||||
while true; do
|
||||
resp=$(nix run --inputs-from . nixpkgs#curl -- -X POST \
|
||||
-H "Authorization: token $CI_BOT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"Do": "merge",
|
||||
"merge_when_checks_succeed": true,
|
||||
"delete_branch_after_merge": true
|
||||
}' \
|
||||
"https://git.clan.lol/api/v1/repos/clan/clan-core/pulls/$pr_number/merge")
|
||||
msg=$(echo $resp | jq -r '.message')
|
||||
if [[ "$msg" != "Please try again later" ]]; then
|
||||
break
|
||||
fi
|
||||
echo "Retrying in 2 seconds..."
|
||||
sleep 2
|
||||
done
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
9
.github/workflows/repo-sync.yml
vendored
9
.github/workflows/repo-sync.yml
vendored
@@ -3,8 +3,10 @@ on:
|
||||
schedule:
|
||||
- cron: "39 * * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
repo-sync:
|
||||
if: github.repository_owner == 'clan-lol'
|
||||
@@ -13,15 +15,10 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.CI_APP_ID }}
|
||||
private-key: ${{ secrets.CI_PRIVATE_KEY }}
|
||||
- name: repo-sync
|
||||
uses: repo-sync/github-sync@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
source_repo: "https://git.clan.lol/clan/clan-core.git"
|
||||
source_branch: "main"
|
||||
|
||||
29
.gitignore
vendored
29
.gitignore
vendored
@@ -1,24 +1,20 @@
|
||||
.direnv
|
||||
.nixos-test-history
|
||||
.hypothesis
|
||||
**/.nixos-test-history
|
||||
***/.hypothesis
|
||||
out.log
|
||||
.coverage.*
|
||||
qubeclan
|
||||
**/qubeclan
|
||||
pkgs/repro-hook
|
||||
testdir
|
||||
**/testdir
|
||||
democlan
|
||||
example_clan
|
||||
result*
|
||||
/pkgs/clan-cli/clan_lib/nixpkgs
|
||||
**/result
|
||||
/pkgs/clan-cli/clan_cli/nixpkgs
|
||||
/pkgs/clan-cli/clan_cli/webui/assets
|
||||
nixos.qcow2
|
||||
*.glade~
|
||||
**/*.glade~
|
||||
/docs/out
|
||||
/pkgs/clan-cli/clan_lib/select
|
||||
.local.env
|
||||
|
||||
# macOS stuff
|
||||
.DS_Store
|
||||
|
||||
# dream2nix
|
||||
.dream2nix
|
||||
@@ -43,14 +39,3 @@ repo
|
||||
node_modules
|
||||
dist
|
||||
.webui
|
||||
|
||||
# TODO: remove after bug in select is fixed
|
||||
select
|
||||
|
||||
# Generated files
|
||||
pkgs/clan-app/ui/api/API.json
|
||||
pkgs/clan-app/ui/api/API.ts
|
||||
pkgs/clan-app/ui/api/Inventory.ts
|
||||
pkgs/clan-app/ui/api/modules_schemas.json
|
||||
pkgs/clan-app/ui/api/schema.json
|
||||
pkgs/clan-app/ui/.fonts
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
nixosModules/clanCore/vars/.* @lopter
|
||||
pkgs/clan-cli/clan_cli/(secrets|vars)/.* @lopter
|
||||
@@ -1,4 +1,23 @@
|
||||
# Contributing to Clan
|
||||
|
||||
<!-- Local file: docs/CONTRIBUTING.md -->
|
||||
Go to the Contributing guide at https://docs.clan.lol/guides/contributing/CONTRIBUTING
|
||||
## Live-reloading documentation
|
||||
|
||||
Enter the `docs` directory:
|
||||
|
||||
```shell-session
|
||||
cd docs
|
||||
```
|
||||
|
||||
Enter the development shell or enable `direnv`:
|
||||
|
||||
```shell-session
|
||||
direnv allow
|
||||
```
|
||||
|
||||
Run a local server:
|
||||
|
||||
```shell-session
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
Open http://localhost:8000/ in your browser.
|
||||
|
||||
@@ -24,7 +24,7 @@ If you're new to Clan and eager to dive in, start with our quickstart guide and
|
||||
|
||||
In the Clan ecosystem, security is paramount. Learn how to handle secrets effectively:
|
||||
|
||||
- **Secrets Management**: Securely manage secrets by consulting [secrets](https://docs.clan.lol/guides/getting-started/secrets/)<!-- [secrets.md](docs/site/guides/getting-started/secrets.md) -->.
|
||||
- **Secrets Management**: Securely manage secrets by consulting [secrets](https://docs.clan.lol/getting-started/secrets/)<!-- [secrets.md](docs/site/getting-started/secrets.md) -->.
|
||||
|
||||
### Contributing to Clan
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
{
|
||||
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")
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfgAAAJDXdRkm13UZ
|
||||
JgAAAAtzc2gtZWQyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfg
|
||||
AAAECIgb2FQcgBKMniA+6zm2cwGre60ATu3Sg1GivgAqVJlI6zj7ubTg6z/aDwRNwvM/Wl
|
||||
QdUocMprQ8E92NWxl6t+AAAAC3BpbnBveEBraXdpAQI=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"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 +0,0 @@
|
||||
../../../users/admin
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVVQjCEuryZii1LmJyjx9DX44eJh3qwTTEWlahYONsz nixbld@kiwi
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1,41 +0,0 @@
|
||||
{ self, pkgs, ... }:
|
||||
{
|
||||
name = "app-ocr-smoke-test";
|
||||
|
||||
enableOCR = true;
|
||||
|
||||
nodes = {
|
||||
wayland =
|
||||
{ modulesPath, ... }:
|
||||
{
|
||||
imports = [ (modulesPath + "/../tests/common/wayland-cage.nix") ];
|
||||
services.cage.program = "${self.packages.${pkgs.system}.clan-app}/bin/clan-app";
|
||||
virtualisation.memorySize = 2047;
|
||||
# TODO: get rid of this and fix debus-proxy error instead
|
||||
services.cage.environment.WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS = "1";
|
||||
};
|
||||
xorg =
|
||||
{ pkgs, modulesPath, ... }:
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/../tests/common/user-account.nix")
|
||||
(modulesPath + "/../tests/common/x11.nix")
|
||||
];
|
||||
virtualisation.memorySize = 2047;
|
||||
services.xserver.enable = true;
|
||||
services.xserver.displayManager.sessionCommands = "${
|
||||
self.packages.${pkgs.system}.clan-app
|
||||
}/bin/clan-app";
|
||||
test-support.displayManager.auto.user = "alice";
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
wayland.wait_for_unit('graphical.target')
|
||||
xorg.wait_for_unit('graphical.target')
|
||||
|
||||
wayland.wait_for_text('Welcome to Clan')
|
||||
xorg.wait_for_text('Welcome to Clan')
|
||||
'';
|
||||
}
|
||||
@@ -5,12 +5,6 @@
|
||||
fileSystems."/".device = "/dev/null";
|
||||
boot.loader.grub.device = "/dev/null";
|
||||
};
|
||||
clan.inventory.services = {
|
||||
borgbackup.test-backup = {
|
||||
roles.client.machines = [ "test-backup" ];
|
||||
roles.server.machines = [ "test-backup" ];
|
||||
};
|
||||
};
|
||||
flake.nixosModules = {
|
||||
test-backup =
|
||||
{
|
||||
@@ -28,30 +22,21 @@
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
# Do not import inventory modules. They should be configured via 'clan.inventory'
|
||||
#
|
||||
# TODO: Configure localbackup via inventory
|
||||
self.clanModules.borgbackup
|
||||
self.clanModules.localbackup
|
||||
];
|
||||
# Borgbackup overrides
|
||||
services.borgbackup.repos.test-backups = {
|
||||
path = "/var/lib/borgbackup/test-backups";
|
||||
authorizedKeys = [ (builtins.readFile ../assets/ssh/pubkey) ];
|
||||
};
|
||||
clan.borgbackup.destinations.test-backup.repo = lib.mkForce "borg@machine:.";
|
||||
|
||||
clan.core.networking.targetHost = "machine";
|
||||
networking.hostName = "machine";
|
||||
services.openssh.settings.UseDns = false;
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
|
||||
programs.ssh.knownHosts = {
|
||||
machine.hostNames = [ "machine" ];
|
||||
machine.publicKey = builtins.readFile ../assets/ssh/pubkey;
|
||||
machine.publicKey = builtins.readFile ../lib/ssh/pubkey;
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.UsePAM = false;
|
||||
settings.UseDns = false;
|
||||
hostKeys = [
|
||||
{
|
||||
path = "/root/.ssh/id_ed25519";
|
||||
@@ -60,35 +45,31 @@
|
||||
];
|
||||
};
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keyFiles = [ ../assets/ssh/pubkey ];
|
||||
|
||||
# This is needed to unlock the user for sshd
|
||||
# Because we use sshd without setuid binaries
|
||||
users.users.borg.initialPassword = "hello";
|
||||
users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
|
||||
|
||||
systemd.tmpfiles.settings."vmsecrets" = {
|
||||
"/root/.ssh/id_ed25519" = {
|
||||
C.argument = "${../assets/ssh/privkey}";
|
||||
C.argument = "${../lib/ssh/privkey}";
|
||||
z = {
|
||||
mode = "0400";
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
"/etc/secrets/ssh.id_ed25519" = {
|
||||
C.argument = "${../assets/ssh/privkey}";
|
||||
C.argument = "${../lib/ssh/privkey}";
|
||||
z = {
|
||||
mode = "0400";
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
"/etc/secrets/borgbackup/borgbackup.ssh" = {
|
||||
C.argument = "${../assets/ssh/privkey}";
|
||||
"/etc/secrets/borgbackup.ssh" = {
|
||||
C.argument = "${../lib/ssh/privkey}";
|
||||
z = {
|
||||
mode = "0400";
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
"/etc/secrets/borgbackup/borgbackup.repokey" = {
|
||||
"/etc/secrets/borgbackup.repokey" = {
|
||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
||||
z = {
|
||||
mode = "0400";
|
||||
@@ -97,7 +78,8 @@
|
||||
};
|
||||
};
|
||||
clan.core.facts.secretStore = "vm";
|
||||
clan.core.vars.settings.secretStore = "vm";
|
||||
# TODO: set this backend as well, once we have implemented it.
|
||||
#clan.core.vars.settings.secretStore = "vm";
|
||||
|
||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
||||
@@ -122,6 +104,7 @@
|
||||
'';
|
||||
folders = [ "/var/test-service" ];
|
||||
};
|
||||
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
||||
|
||||
fileSystems."/mnt/external-disk" = {
|
||||
device = "/dev/vdb"; # created in tests with virtualisation.emptyDisks
|
||||
@@ -142,27 +125,28 @@
|
||||
touch /run/unmount-external-disk
|
||||
'';
|
||||
};
|
||||
|
||||
services.borgbackup.repos.test-backups = {
|
||||
path = "/var/lib/borgbackup/test-backups";
|
||||
authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
|
||||
};
|
||||
};
|
||||
};
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
clanCore = self.checks.x86_64-linux.clan-core-for-checks;
|
||||
in
|
||||
{
|
||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
||||
backups = self.clanLib.test.containerTest {
|
||||
name = "backups";
|
||||
# Needs investigation on aarch64-linux
|
||||
# vm-test-run-test-backups> qemu-kvm: No machine specified, and there is no default
|
||||
# vm-test-run-test-backups> Use -machine help to list supported machines
|
||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux && pkgs.stdenv.hostPlatform.system != "aarch64-linux") {
|
||||
test-backups = (import ../lib/test-base.nix) {
|
||||
name = "test-backups";
|
||||
nodes.machine = {
|
||||
imports =
|
||||
[
|
||||
self.nixosModules.clanCore
|
||||
# Some custom overrides for the backup tests
|
||||
self.nixosModules.test-backup
|
||||
]
|
||||
++
|
||||
# import the inventory generated nixosModules
|
||||
self.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
||||
imports = [
|
||||
self.nixosModules.clanCore
|
||||
self.nixosModules.test-backup
|
||||
];
|
||||
virtualisation.emptyDiskImages = [ 256 ];
|
||||
clan.core.settings.directory = ./.;
|
||||
};
|
||||
|
||||
@@ -175,14 +159,14 @@
|
||||
machine.succeed("echo testing > /var/test-backups/somefile")
|
||||
|
||||
# create
|
||||
machine.succeed("clan backups create --debug --flake ${clanCore} test-backup")
|
||||
machine.succeed("clan backups create --debug --flake ${self} test-backup")
|
||||
machine.wait_until_succeeds("! systemctl is-active borgbackup-job-test-backup >&2")
|
||||
machine.succeed("test -f /run/mount-external-disk")
|
||||
machine.succeed("test -f /run/unmount-external-disk")
|
||||
|
||||
# list
|
||||
backup_id = json.loads(machine.succeed("borg-job-test-backup list --json"))["archives"][0]["archive"]
|
||||
out = machine.succeed("clan backups list --debug --flake ${clanCore} test-backup").strip()
|
||||
out = machine.succeed("clan backups list --debug --flake ${self} test-backup").strip()
|
||||
print(out)
|
||||
assert backup_id in out, f"backup {backup_id} not found in {out}"
|
||||
localbackup_id = "hdd::/mnt/external-disk/snapshot.0"
|
||||
@@ -190,7 +174,7 @@
|
||||
|
||||
## borgbackup restore
|
||||
machine.succeed("rm -f /var/test-backups/somefile")
|
||||
machine.succeed(f"clan backups restore --debug --flake ${clanCore} test-backup borgbackup 'test-backup::borg@machine:.::{backup_id}' >&2")
|
||||
machine.succeed(f"clan backups restore --debug --flake ${self} test-backup borgbackup 'test-backup::borg@machine:.::{backup_id}' >&2")
|
||||
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
|
||||
machine.succeed("test -f /var/test-service/pre-restore-command")
|
||||
machine.succeed("test -f /var/test-service/post-restore-command")
|
||||
@@ -198,7 +182,7 @@
|
||||
|
||||
## localbackup restore
|
||||
machine.succeed("rm -rf /var/test-backups/somefile /var/test-service/ && mkdir -p /var/test-service")
|
||||
machine.succeed(f"clan backups restore --debug --flake ${clanCore} test-backup localbackup '{localbackup_id}' >&2")
|
||||
machine.succeed(f"clan backups restore --debug --flake ${self} test-backup localbackup '{localbackup_id}' >&2")
|
||||
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
|
||||
machine.succeed("test -f /var/test-service/pre-restore-command")
|
||||
machine.succeed("test -f /var/test-service/post-restore-command")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(
|
||||
(import ../lib/test-base.nix) (
|
||||
{ ... }:
|
||||
{
|
||||
name = "borgbackup";
|
||||
@@ -12,22 +12,23 @@
|
||||
{
|
||||
services.openssh.enable = true;
|
||||
services.borgbackup.repos.testrepo = {
|
||||
authorizedKeys = [ (builtins.readFile ../assets/ssh/pubkey) ];
|
||||
authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
|
||||
};
|
||||
}
|
||||
{
|
||||
clan.core.settings.machine.name = "machine";
|
||||
clan.core.settings.directory = ./.;
|
||||
clan.core.state.testState.folders = [ "/etc/state" ];
|
||||
environment.etc.state.text = "hello world";
|
||||
systemd.tmpfiles.settings."vmsecrets" = {
|
||||
"/etc/secrets/borgbackup/borgbackup.ssh" = {
|
||||
C.argument = "${../assets/ssh/privkey}";
|
||||
"/etc/secrets/borgbackup.ssh" = {
|
||||
C.argument = "${../lib/ssh/privkey}";
|
||||
z = {
|
||||
mode = "0400";
|
||||
user = "root";
|
||||
};
|
||||
};
|
||||
"/etc/secrets/borgbackup/borgbackup.repokey" = {
|
||||
"/etc/secrets/borgbackup.repokey" = {
|
||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
||||
z = {
|
||||
mode = "0400";
|
||||
@@ -35,8 +36,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
# clan.core.facts.secretStore = "vm";
|
||||
clan.core.vars.settings.secretStore = "vm";
|
||||
clan.core.facts.secretStore = "vm";
|
||||
|
||||
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{ fetchgit }:
|
||||
fetchgit {
|
||||
url = "https://git.clan.lol/clan/clan-core.git";
|
||||
rev = "4b44892b476b32b1ca7d2f637da41562b8ed5456";
|
||||
sha256 = "15k7dp3m9dcvx75sm900xp70vss2pnx0ywjnpmqyqlmwpvzn663p";
|
||||
}
|
||||
@@ -1,44 +1,19 @@
|
||||
(
|
||||
(import ../lib/container-test.nix) (
|
||||
{ ... }:
|
||||
{
|
||||
name = "container";
|
||||
name = "secrets";
|
||||
|
||||
nodes.machine1 =
|
||||
nodes.machine =
|
||||
{ ... }:
|
||||
{
|
||||
networking.hostName = "machine1";
|
||||
networking.hostName = "machine";
|
||||
services.openssh.enable = true;
|
||||
services.openssh.startWhenNeeded = false;
|
||||
};
|
||||
|
||||
nodes.machine2 =
|
||||
{ ... }:
|
||||
{
|
||||
networking.hostName = "machine2";
|
||||
services.openssh.enable = true;
|
||||
services.openssh.startWhenNeeded = false;
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import subprocess
|
||||
start_all()
|
||||
machine1.succeed("systemctl status sshd")
|
||||
machine2.succeed("systemctl status sshd")
|
||||
machine1.wait_for_unit("sshd")
|
||||
machine2.wait_for_unit("sshd")
|
||||
|
||||
p1 = subprocess.run(["ip", "a"], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
assert p1.returncode == 0
|
||||
bridge_output = p1.stdout.decode("utf-8")
|
||||
assert "br0" in bridge_output, f"bridge not found in ip a output: {bridge_output}"
|
||||
|
||||
for m in [machine1, machine2]:
|
||||
out = machine1.succeed("ip addr show eth1")
|
||||
assert "UP" in out, f"UP not found in ip addr show output: {out}"
|
||||
assert "inet" in out, f"inet not found in ip addr show output: {out}"
|
||||
assert "inet6" in out, f"inet6 not found in ip addr show output: {out}"
|
||||
|
||||
machine1.succeed("ping -c 1 machine2")
|
||||
machine.succeed("systemctl status sshd")
|
||||
machine.wait_for_unit("sshd")
|
||||
'';
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
{
|
||||
pkgs,
|
||||
self,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
clanLib.test.makeTestClan {
|
||||
inherit pkgs self;
|
||||
nixosTest = (
|
||||
{ lib, ... }:
|
||||
let
|
||||
machines = [
|
||||
"admin"
|
||||
"peer"
|
||||
"signer"
|
||||
];
|
||||
in
|
||||
{
|
||||
name = "data-mesher";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
inventory = {
|
||||
machines = lib.genAttrs machines (_: { });
|
||||
services = {
|
||||
data-mesher.default = {
|
||||
roles.peer.machines = [ "peer" ];
|
||||
roles.admin.machines = [ "admin" ];
|
||||
roles.signer.machines = [ "signer" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
defaults =
|
||||
{ config, ... }:
|
||||
{
|
||||
environment.systemPackages = [
|
||||
config.services.data-mesher.package
|
||||
];
|
||||
|
||||
clan.data-mesher.network.interface = "eth1";
|
||||
clan.data-mesher.bootstrapNodes = [
|
||||
"[2001:db8:1::1]:7946" # peer1
|
||||
"[2001:db8:1::2]:7946" # peer2
|
||||
];
|
||||
|
||||
# speed up for testing
|
||||
services.data-mesher.settings = {
|
||||
cluster.join_interval = lib.mkForce "2s";
|
||||
cluster.push_pull_interval = lib.mkForce "5s";
|
||||
};
|
||||
};
|
||||
|
||||
nodes = {
|
||||
admin.clan.data-mesher.network.tld = "foo";
|
||||
};
|
||||
|
||||
# TODO Add better test script.
|
||||
testScript = ''
|
||||
|
||||
def resolve(node, success = {}, fail = [], timeout = 60):
|
||||
for hostname, ips in success.items():
|
||||
for ip in ips:
|
||||
node.wait_until_succeeds(f"getent ahosts {hostname} | grep {ip}", timeout)
|
||||
|
||||
for hostname in fail:
|
||||
node.wait_until_fails(f"getent ahosts {hostname}")
|
||||
|
||||
start_all()
|
||||
|
||||
admin.wait_for_unit("data-mesher")
|
||||
signer.wait_for_unit("data-mesher")
|
||||
peer.wait_for_unit("data-mesher")
|
||||
|
||||
# check dns resolution
|
||||
for node in [admin, signer, peer]:
|
||||
resolve(node, {
|
||||
"admin.foo": ["2001:db8:1::1", "192.168.1.1"],
|
||||
"peer.foo": ["2001:db8:1::2", "192.168.1.2"],
|
||||
"signer.foo": ["2001:db8:1::3", "192.168.1.3"]
|
||||
})
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"publickey": "age10zxkj45fah3qa8uyg3a36jsd06d839xfq64nrez9etrsf4km0gtsp45gsz",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"publickey": "age1faqrml2ukc6unfm75d3v2vnaf62v92rdxaagg3ty3cfna7vt99gqlzs43l",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"publickey": "age153mke8v2qksyqjc7vta7wglzdqr5epazt83nch0ur5v7kl87cfdsr07qld",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:7xyb6WoaN7uRWEO8QRkBw7iytP5hFrA94VRi+sy/UhzqT9AyDPmxB/F8ASFsBbzJUwi0Oqd2E1CeIYRoDhG7JHnDyL2bYonz2RQ=,iv:slh3x774m6oTHAXFwcen1qF+jEchOKCyNsJMbNhqXHE=,tag:wtK8H8PZCESPA1vZCd7Ptw==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPTzZ4RTVNb2I1MTBRMEcy\neU1Eek9GakkydEJBVm9kR3AyY1pEYkorNUYwCkh2WHhNQmc1eWI2cCtEUFFWdzJq\nS0FvQWtoOFkzRVBxVzhuczc0aVprbkkKLS0tIFRLdmpnbzY1Uk9LdklEWnQzZHM2\nVEx3dzhMSnMwaWE0V0J6VTZ5ZVFYMjgKdaICa/hprHxhH89XD7ri0vyTT4rM+Si0\niHcQU4x64dgoJa4gKxgr4k9XncjoNEjJhxL7i/ZNZ5deaaLRn5rKMg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:24:55Z",
|
||||
"mac": "ENC[AES256_GCM,data:TJWDHGSRBfOCW8Q+t3YxG3vlpf9a5u7B27AamnOk95huqIv0htqWV3RuV7NoOZ5v2ijqSe/pLfpwrmtdhO2sUBEvhdhJm8UzLShP7AbH9lxV+icJOsY7VSrp+R5W526V46ONP6p47b7fOQBbp03BMz01G191N68WYOf6k2arGxU=,iv:nEyTBwJ2EA+OAl8Ulo5cvFX6Ow2FwzTWooF/rdkPiXg=,tag:oYcG16zR+Fb5XzVsHhq2Qw==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../users/admin
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:JOOhvl0clDD/b5YO45CXR3wVopBSNe9dYBG+p5iD+nniN2OgOwBgYPNSCVtc+NemqutD12hFUSfCzXidkv0ijhD1JZeLar9Ygxc=,iv:XctQwSYSvKhDRk/XMacC9uMydZ8e9hnhpoWTgyXiFI0=,tag:foAhBlg4DwpQU2G9DzTo5g==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBVWMvWkp5TnZQcGs5Ykhp\nWC91YkoyZERqdXpxQm5JVmRhaUhueEJETDJVCkM4V0hSYldkV1U2Q0d1TGh3eGNR\nVjJ1VFd6ZEN0SXZjSVEvcnV2WW0vbVUKLS0tIFRCNW9nWHdYaUxLSVVUSXM0OGtN\nVFMzRXExNkYxcFE3QWlxVUM3ay9INm8KV6r8ftpwarly3qXoU9y8KxKrUKLvP9KX\nGsP0pORsaM+qPMsdfEo35CqhAeQu0+6DWd7/67+fUMp6Jr0DthtTmg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:25:28Z",
|
||||
"mac": "ENC[AES256_GCM,data:scY9+/fcXhfHEdrsZJLOM6nfjpRaURgTVbCRepUjhUo24B4ByEsAo2B8psVAaGEHEsFRZuoiByqrGzKhyUASmUs+wn+ziOKBTLzu55fOakp8PWYtQ4miiz2TQffp80gCQRJpykcbUgqIKXNSNutt4tosTBL7osXwCEnEQWd+SaA=,iv:1VXNvLP6DUxZYEr1juOLJmZCGbLp33DlwhxHQV9AMD4=,tag:uFM1R8OmkFS74/zkUG0k8A==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../users/admin
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:i1YBJdK8XmWnVnZKBpmWggSN8JSOr8pm2Zx+CeE8qqeLZ7xwMO8SYCutM8l94M5vzmmX0CmwzeMZ/JVPbEwFd3ZAImUfh685HOY=,iv:N4rHNaX+WmoPb0EZPqMt+CT1BzaWO9LyoemBxKn+u/s=,tag:PnzSvdGwVnTMK8Do8VzFaQ==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4RXlmcVNGTnlkY2ZqZFlH\nVnh0eHhRNE5hRDNDVkt0TEE0bmRNN2JIVkN3CkxnaGM4Y3M3a0xoK2xMRzBLMHRV\nT1FzKzNRMFZOeWc2K3E5K2FzdUsvWmsKLS0tIENtVlFSWElHN3RtOUY2alhxajhs\naXI1MmR4WC9EVGVFK3dHM1gvVnlZMVUKCyLz0DkdbWfSfccShO1xjWfxhunEIbD0\n6imeIBhZHvVJmZLXnVl7B0pNXo6be7WSBMAUM9gUtCNh4zaChBNwGw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:25:52Z",
|
||||
"mac": "ENC[AES256_GCM,data:WFGysoXN95e/RxL094CoL4iueqEcSqCSQZLahwz9HMLi+8HWZIXr55a+jyK7piqR8nBS4BquU5fKhlC6BvEbZFt69t4onTA+LxS3D7A8/TO0CWS0RymUjW9omJUseRQWwAHtE7l0qI5hdOUKhQ+o5pU+2bc3PUlaONM0aOCCoFo=,iv:l1f4aVqLl5VAMfjNxDbxQEQp/qY/nxzgv2GTuPVBoBA=,tag:4PPDCmDrviqdn42RLHQYbA==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../users/admin
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/admin
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:w3bU23Pfe8W89lF+tOmEYPU/A4FkY6n7rgQ6yo+eqCJFxTyHydV6Mg4/g4jaL+4wwIqNYRiMR8J8jLhSvw3Bc59u7Ul+RGwdpiKoBBJfsHjO8r6uOz2u9Raa+iUJH1EJWmGvsQXAILpliZ+klS96VWnGN3pYMEI=,iv:7QbUxta6NPQLZrh6AOcNe+0wkrADuTI9VKVp8q+XoZ8=,tag:ZH0t3RylfQk5U23ZHWaw0g==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age10zxkj45fah3qa8uyg3a36jsd06d839xfq64nrez9etrsf4km0gtsp45gsz",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKaTBoSFJVSTdZeW4wZG9p\nWFR1LzVmYS8xWmRqTlNtWFVkSW9jZXpVejJBCkpqZm12L1dDSmNhekVsK1JBOU9r\nZThScGdDakFlRzNsVXp1eE5yOStFSW8KLS0tIFRrTkZBQlRsR2VNcUJvNEkzS2pw\nNksvM296UkFWTkZDVVp1ZVZMNUs4cWsKWTteB1G9Oo38a81PeqKO09NUQetuqosC\nhrToQ6NMo5O7/StmVG228MHbJS3KLXsvh2AFOEPyZrbpB2Opd2wwoA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6U2FWRThRNkVQdk9yZ0VE\nM09iSVhmeldMcDZVaFRDNGtjWTdBa0VIT2pJCkdtd04xSXdicDY3OHI1WXl5TndB\nemtQeW1SS2tVVllPUHhLUTRla3haZGMKLS0tIGN0NVNEN3RKeWM0azBBMnBpQU4r\nTFFzQ0lOcGt0ek9UZmZZRjhibTNTc0EKReUwYBVM1NKX0FD/ZeokFAAknwju5Azq\nGzl4UVJBi5Es0GWORdCGElPXMd7jMud1SwgY04AdZj/dzinCSW4CZw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:25:10Z",
|
||||
"mac": "ENC[AES256_GCM,data:0vl9Gt4QeH+GJcnl8FuWSaqQXC8S6Pe50NmeDg5Nl2NWagz8aLCvOFyTqX/Icp/bTi1XQ5icHHhF3YhM+QAvdUL3aO0WGbh92dPRnFuvlZsdtwCFhT+LyHyYHFf6yP+0h/uFpJv9fE6xY22CezA6ZVQ8ywi1epaC548Gr27uVe4=,iv:G4hZVCLkIpbg9uwB7Y8xtHLdnlmBvFrPjxSoqdyHNvM=,tag:uvKwakhUY2aa7v0tmR/o8A==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1,3 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAm204bpSFi4jOjZuXDpIZ/rcJBrbG4zAc7OSA4rAVSYE=
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/peer
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:kERPY40pyvke0mRBnafa4zOaF46rbueRbhpUCXjYP5ORpC7zoOhbdlVBhOsPqE2vfEP4RWkH+ZPdDYXOKXwotBCmlq2i7TfZeoNXFkzWXc3GyM5mndnjCc8hvYEQF1w6xkkVSUt4n06BAw/gT0ppz+vo5dExIA8=,iv:JmYD2o4DGqds6DV7ucUmUD0BRB61exbRsNAtINOR8cQ=,tag:Z58gVnHD+4s21Z84IRw+Vw==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1faqrml2ukc6unfm75d3v2vnaf62v92rdxaagg3ty3cfna7vt99gqlzs43l",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4OFluVThBdUJSTmRVTk94\neFZnLytvcnNSdmQvR3ZkT2UvWFVieFV1SUFNCm9jWHlyZXRwaVdFaG9ocnd4S3FU\ndTZ2dklBbkFVL0hVT0Y2L1o5dnUyNG8KLS0tIGFvYlBJR3l2b3F6OU9uMTFkYjli\nNVFLOWQzOStpU2kzb0xyZUFCMnBmMVUK5Jzssf1XBX25bq0RKlJY8NwtKIytxL/c\nBPPFDZywJiUgw1izsdfGVkRhhSFCQIz+yWIJWzr01NU2jLyFjSfCNw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzYW92c3Q4SktwSnJ1TkRJ\nZEJyZk96cG8ybkpPQzYzVk0xZGs0eCtISVR3CmhDaWxTem1FMjJKNmZNaTkxN01n\nenUvdFI1UkFmL1lzNlM5N0Ixd0dpc1EKLS0tIHpyS2VHaHRRdUovQVgvRmRHaXh3\naFpSNURjTWkxaW9TOXpKL2IvcUFEbmMKq4Ch7DIL34NetFV+xygTdcpQjjmV8v1n\nlvYcjUO/9c3nVkxNMJYGjuxFLuFc4Gw+AyawCjpsIYXRskYRW4UR1w==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:25:43Z",
|
||||
"mac": "ENC[AES256_GCM,data:YhL2d6i0VpUd15B4ow2BgRpyEm0KEA8NSb7jZcjI58d7d4lAqBMcDQB+8a9e2NZbPk8p1EYl3q4VXbEnuwsJiPZI2kabRusy/IGoHzUTUMFfVaOuUcC0eyINNVSmzJxnCbLCAA1Aj1yXzgRQ0MWr7r0RHMKw0D1e0HxdEsuAPrA=,iv:yPlMmE6+NEEQ9uOZzD3lUTBcfUwGX/Ar+bCu0XKnjIg=,tag:eR22BCFVAlRHdggg9oCeaA==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1,3 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAv5dICFue2fYO0Zi1IyfYjoNfR6713WpISo7+2bSjL18=
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/signer
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:U8F7clQ2Tuj8zy5EoEga/Mc9N3LLZrlFf5m7UJKrP5yybFRCJSBs05hOcNe+LQZdEAvvr0Qbkry1pQyE84gCVbxHvwkD+l3GbguBuLMsW96bHcmstb6AvZyhMDBpm73Azf4lXhNaiB8p2pDWdxV77E+PPw1MNYI=,iv:hQhN6Ak8tB6cXSCnTmmQqHEpXWpWck3uIVCk5pUqFqU=,tag:uC4ljcs92WPlUOfwSkrK9Q==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age153mke8v2qksyqjc7vta7wglzdqr5epazt83nch0ur5v7kl87cfdsr07qld",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvV05lejQrdUQvQjZPOG9v\nZ01naXlYZ1JxWHhDT1M1aUs1RWJDSU1acVFFCmdHY094aGRPYWxpdVVxSFVHRU9v\nNnVaeTlpSEdtSWRDMmVMSjdSOEQ4ZlEKLS0tIFo5NVk2bzBxYjZ5ZWpDWTMrQ2VF\nVThWUk0rVXpTY2svSCtiVDhTQ2kvbFkKEM2DBuFtdEj1G/vS1TsyIfQxSFFvPTDq\nCmO7L/J5lHdyfIXzp/FlhdKpjvmchb8gbfJn7IWpKopc7Zimy/JnGQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArNzVUaHkzUzVEMlh1Q3Qr\nOEo0aDJIMG91amJiZG50MEhqblRCTWxRRVVRCk4xZlp4SkJuUHc2UnFyU1prczkz\nNGtlQlRlNnBDRFFvUGhReTh6MTBZaXMKLS0tIGxtaXhUMDM0RU4yQytualdzdTFt\nWGRiVG54MnYrR2lqZVZoT0VkbmV5WUUKbzAnOkn8RYOo7z4RISQ0yN875vSEQMDa\nnnttzVrQuK0/iZvzJ0Zq8U9+JJJKvFB1tHqye6CN0zMbv55CLLnA0g==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:26:07Z",
|
||||
"mac": "ENC[AES256_GCM,data:uMss4+BiVupFqX7nHnMo+0yZ8RPuFD8VHYK2EtJSqzgurQrZVT4tJwY50mz2gVmwbrm49QYKk5S+H29DU0cM0HiEOgB5P5ObpXTRJPagWQ48CEFrDpBzLplobxulwnN6jJ1dpL3JF3jfrzrnSDFXMvx+n5x/86/AYXYRsi/UeyY=,iv:mPT1svKrNGmYpbL9hh2Bxxakml69q+U6gQ0ZnEcbEyg=,tag:zcZx1lTw/bEsX/1g+6T04g==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1,3 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAeUkW5UIwA1svbNY71ePyJKX68UhxrqIUGQ2jd06w5WM=
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../sops/machines/admin
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../sops/machines/peer
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../sops/machines/signer
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:nRlCMF58cnkdUAE2aVHEG1+vAckKtVt48Jr21Bklfbsqe1yTiHPFAMLL1ywgWWWd7FjI/Z8WID9sWzh9J8Vmotw4aJWU/rIQSeF8cJHALvfOxarJIIyb7purAiPoPPs6ggGmSmVFGB1aw8kH1JMcppQN8OItdQM=,iv:qTwaL2mgw6g7heN/H5qcjei3oY+h46PdSe3v2hDlkTs=,tag:jYNULrOPl9mcQTTrx1SDeA==,type:str]",
|
||||
"sops": {
|
||||
"kms": null,
|
||||
"gcp_kms": null,
|
||||
"azure_kv": null,
|
||||
"hc_vault": null,
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age153mke8v2qksyqjc7vta7wglzdqr5epazt83nch0ur5v7kl87cfdsr07qld",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRcG44cGFBWXk2Z0pmNklv\nTnJ5b0svLytzZmNNRkxCVU1zaDVhNUs2cld3CklsenpWd0g2OEdKKzBMQlNEejRn\nTlEvY01HYjdvVExadnN3aXZIRTZ4YlEKLS0tIGRPUXdNSHZCRDBMbno2MjJqRHBl\nSzdiSURDYitQWFpaSElkdmdicDVjMWsKweQiRqyzXmzabmU2fmgwHtOa9uDmhx9O\ns9NfUhC3ifooQUSeYp58b1ZGJQx5O5bn9q/DaEoit5LTOUprt1pUPA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiTEdlL29sVWFpSDNNaXRJ\ndTJDRkU4VzFPQ0M4MkFha2IxV2FXN2o3ZEFRCjF3UnZ5U1hTc3VvSTIzcWxOZjl0\ncHlLVEFqRk1UbGdxaUxEeDFqbFVYaU0KLS0tIFFyMnJkZnRHdWg4Z1IyRHFkY0I5\nQjdIMGtGLzRGMFM0ektDZ3hzZDdHSmMKvxOQuKgePom0QfPSvn+4vsGHhJ4BoOvW\nc27Vn4/i4hbjfJr4JpULAwyIwt3F0RaTA2M6EkFkY8otEi3vkcpWvA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age10zxkj45fah3qa8uyg3a36jsd06d839xfq64nrez9etrsf4km0gtsp45gsz",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5ZzdsaVRnSmsrMGR1Ylg3\nZkpscTdwNUl5NUVXN3kvMU1icE0yZU1WSEJBClB6SlJYZUhDSElRREx5b0VueFUw\nNVFRU3BSU24yWEtpRnJoUC83SDVaUWsKLS0tIGVxNEo3TjlwakpDZlNsSkVCOXlz\nNDgwaE1xNjZkSnJBVlU5YXVHeGxVNFEKsXKyTzq9VsERpXzbFJGv/pbAghFAcXkf\nMmCgQHsfIMBJQUstcO8sAkxv3ced0dAEz8O6NUd0FS2zlhBzt29Rnw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1faqrml2ukc6unfm75d3v2vnaf62v92rdxaagg3ty3cfna7vt99gqlzs43l",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkK1hDMGxCc1IvYXlJMnBF\nWncxaXBQa1RpTWdwUHc3Yk16My8rVHNJc2dFCkNlK2h0dy9oU3Z5ZGhwRWVLYVUz\ncVBKT2x5VnlhbXNmdHkwbmZzVG5sd0EKLS0tIHJaMzhDanF4Rkl3akN4MEIxOHFC\nYWRUZ08xb1UwOFNRaktkMjIzNXZmNkUK1rlbJ96oUNQZLmCmPNDOKxfDMMa+Bl2E\nJPxcNc7XY3WBHa3xFUbcqiPxWxDyaZjhq/LYQGpepiGonGMEzR5JOQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-08T13:25:20Z",
|
||||
"mac": "ENC[AES256_GCM,data:za9ku+9lu1TTRjbPcd5LYDM4tJsAYF/yuWFCGkAhqcYguEducsIfoKBwL42ahAzqLjCZp91YJuINtw16mM+Hmlhi/BVwhnXNHqcfnKoAS/zg9KJvWcvXwKMmjEjaBovqaCWXWoKS7dn/wZ7nfGrlsiUilCDkW4BzTIzkqNkyREU=,iv:2X9apXMatwCPRBIRbPxz6PJQwGrlr7O+z+MrsnFq+sQ=,tag:IYvitoV4MhyJyRO1ySxbLQ==,type:str]",
|
||||
"pgp": null,
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../sops/users/admin
|
||||
@@ -1,3 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEA/5j+Js7oxwWvZdfjfEO/3UuRqMxLKXsaNc3/5N2WSaw=
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -10,6 +10,7 @@
|
||||
self.clanModules.deltachat
|
||||
self.nixosModules.clanCore
|
||||
{
|
||||
clan.core.settings.machine.name = "machine";
|
||||
clan.core.settings.directory = ./.;
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
{
|
||||
...
|
||||
}:
|
||||
{
|
||||
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
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
{
|
||||
pkgs,
|
||||
self,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
clanLib.test.makeTestClan {
|
||||
inherit pkgs self;
|
||||
nixosTest = (
|
||||
{ ... }:
|
||||
{
|
||||
# This tests the compatibility of the inventory
|
||||
# With the test framework
|
||||
# - legacy-modules
|
||||
# - clan.service modules
|
||||
name = "dummy-inventory-test";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
inventory = {
|
||||
machines.peer1 = { };
|
||||
machines.admin1 = { };
|
||||
services = {
|
||||
legacy-module.default = {
|
||||
roles.peer.machines = [ "peer1" ];
|
||||
roles.admin.machines = [ "admin1" ];
|
||||
};
|
||||
};
|
||||
|
||||
instances."test" = {
|
||||
module.name = "new-service";
|
||||
roles.peer.machines.peer1 = { };
|
||||
};
|
||||
|
||||
modules = {
|
||||
legacy-module = ./legacy-module;
|
||||
};
|
||||
};
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
# nix run .#generate-test-vars -- checks/dummy-inventory-test dummy-inventory-test
|
||||
clan.core.vars.generators.new-service = {
|
||||
files.not-a-secret = {
|
||||
secret = false;
|
||||
deploy = true;
|
||||
};
|
||||
files.a-secret = {
|
||||
secret = true;
|
||||
deploy = true;
|
||||
owner = "nobody";
|
||||
group = "users";
|
||||
mode = "0644";
|
||||
};
|
||||
script = ''
|
||||
# This is a dummy script that does nothing
|
||||
echo -n "not-a-secret" > $out/not-a-secret
|
||||
echo -n "a-secret" > $out/a-secret
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
''
|
||||
start_all()
|
||||
admin1.wait_for_unit("multi-user.target")
|
||||
peer1.wait_for_unit("multi-user.target")
|
||||
# Provided by the legacy module
|
||||
print(admin1.succeed("systemctl status dummy-service"))
|
||||
print(peer1.succeed("systemctl status dummy-service"))
|
||||
|
||||
# peer1 should have the 'hello' file
|
||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.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,10 +0,0 @@
|
||||
---
|
||||
description = "Set up dummy-module"
|
||||
categories = ["System"]
|
||||
features = [ "inventory" ]
|
||||
|
||||
[constraints]
|
||||
roles.admin.min = 1
|
||||
roles.admin.max = 1
|
||||
---
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
imports = [
|
||||
../shared.nix
|
||||
];
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
imports = [
|
||||
../shared.nix
|
||||
];
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
systemd.services.dummy-service = {
|
||||
enable = true;
|
||||
description = "Dummy service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
generated_password_path="${config.clan.core.vars.generators.dummy-generator.files.generated-password.path}"
|
||||
if [ ! -f "$generated_password_path" ]; then
|
||||
echo "Generated password file not found: $generated_password_path"
|
||||
exit 1
|
||||
fi
|
||||
host_id_path="${config.clan.core.vars.generators.dummy-generator.files.host-id.path}"
|
||||
if [ ! -e "$host_id_path" ]; then
|
||||
echo "Host ID file not found: $host_id_path"
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
# TODO: add and prompt and make it work in the test framework
|
||||
clan.core.vars.generators.dummy-generator = {
|
||||
files.host-id.secret = false;
|
||||
files.generated-password.secret = true;
|
||||
script = ''
|
||||
echo $RANDOM > "$out"/host-id
|
||||
echo $RANDOM > "$out"/generated-password
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:GPpsUhSzWPtTP8EUNKsobFXjYqDldhkkIH6hBk11RsDLAGWdhVrwcISGbhsWpYhvAdPKA84DB6Zqyh9lL2bLM9//ybC1kzY20BQ=,iv:NrxMLdedT2FCkUAD00SwsAHchIsxWvqe7BQekWuJcxw=,tag:pMDXcMyHnLF2t3Qhb1KolA==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzb2tWb1ExKzdmUTRzaGVj\nK3cyYTBHZTJwVjM1SzUvbHFiMnVhY05iKzFZCnJTSE1VSVdpcUFLSEJuaE1CZzJD\nWjZxYzN2cUltdThNMVRKU3FIb20vUXMKLS0tIFlHQXRIdnMybDZFUVEzWlQrc1dw\nbUxhZURXblhHd0pka0JIK1FTZEVqdUEKI/rfxQRBc+xGRelhswkJQ9GcZs6lzfgy\nuCxS5JI9npdPLQ/131F3b21+sP5YWqks41uZG+vslM1zQ+BlENNhDw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-04T12:44:13Z",
|
||||
"mac": "ENC[AES256_GCM,data:fWxLHXBWolHVxv6Q7utcy6OVLV13ziswrIYyNKiwy1vsU8i7xvvuGO1HlnE+q43D2WuHR53liKq1UHuf1JMrWzTwZ0PYe+CVugtoEtbR2qu3rK/jAkOyMyhmmHzmf6Rp4ZMCzKgZeC/X2bDKY/z0firHAvjWydEyogutHpvtznM=,iv:OQI3FfkLneqbdztAXVQB3UkHwDPK+0hWu5hZ9m8Oczg=,tag:em6GfS2QHsXs391QKPxfmA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../users/admin
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:W3cOkUYL5/YulW2pEISyTlMaA/t7/WBE7BoCdFlqrqgaCL7tG4IV2HgjiPWzIVMs0zvDSaghdEvAIoB4wOf470d1nSWs0/E8SDk=,iv:wXXaZIw3sPY8L/wxsu7+C5v+d3RQRuwxZRP4YLkS8K4=,tag:HeK4okj7O7XDA9JDz2KULw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxRC83b3dtSVpXcGovNnVs\nTzFka2J2MEFhYkF1ajVrdjMrNUtPWGRObjM4Cm5zSUR5OGw0T0FaL3BaWmR6L29W\nU2syMFIyMUhFRUZpWFpCT28vWko2ZU0KLS0tIFpHK3BjU1V1L0FrMGtwTGFuU3Mz\nRkV5VjI2Vndod202bUR3RWQwNXpmVzQKNk8/y7M62wTIIKqY4r3ZRk5aUCRUfine\n1LUSHMKa2bRe+hR7nS7AF4BGXp03h2UPY0FP5+U5q8XuIj1jfMX8kg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-04T12:44:16Z",
|
||||
"mac": "ENC[AES256_GCM,data:yTkQeFvKrN1+5FP+yInsaRWSAG+ZGG0uWF3+gVRvzJTFxab8kT2XkAMc+4D7SKgcjsmwBBb77GNoAKaKByhZ92UaCfZ2X66i7ZmYUwLM1NVVmm+xiwwjsh7PJXlZO/70anTzd1evtlZse0jEmRnV5Y0F0M6YqXmuwU+qGUJU2F8=,iv:sy6ozhXonWVruaQfa7pdEoV5GkNZR/UbbINKAPbgWeg=,tag:VMruQ1KExmlMR7TsGNgMlg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../users/admin
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/admin1
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:T8edCvw=,iv:7/G5xt5fv38I9uFzk7WMIr9xQdz/6lFxqOC+18HBg8Q=,tag:F39Cxbgmzml+lZLsZ59Kmg==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPNUhiYkZWK3dPMHNiRTVM\nRHNvaHFsOFp1c0UxQitwVG0zY01MNDZRV1E4CjEybENoTVIzN29vQ3FtUTRSYmFU\nNXIzQllVSllXRGN2M1B6WXJLdHZSajgKLS0tIDllZ0ZmZUcxMHhDQUpUOEdWbmkv\neUQweHArYTdFSmNteVpuQ3BKdnh0Y0UKs8Hm3D+rXRRfpUVSZM3zYjs6b9z8g10D\nGTkvreUMim4CS22pjdQ3eNA9TGeDXfWXE7XzwXLCb+wVcf7KwbDmvg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKSDhpT3cvck9PenZYVEZH\ndFQreVRBdG93L1dBUGlvYjFWcDlHWUJsZUVBCm9DMTJ4UytiYzlEVHNWdUcwS1ds\nT0dhbzAzNDdmbDBCU0dvL2xNeHpXcGsKLS0tIFArbmpsbzU3WnpJdUt1MGN0L1d0\nV1JkTDJYWUxsbmhTQVNOeVRaSUhTODQKk9Vph2eldS5nwuvVX0SCsxEm4B+sO76Z\ndIjJ3OQxzoZmXMaOOuKHC5U0Y75Qn7eXC43w5KHsl2CMIUYsBGJOZw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-04T12:44:14Z",
|
||||
"mac": "ENC[AES256_GCM,data:6fKrS1eLLUWlHkQpxLFXBRk6f2wa5ADLMViVvYXXGU24ayl9UuNSKrCRHp9cbzhqhti3HdwyNt6TM+2X6qhiiAQanKEB2PF7JRYX74NfNKil9BEDjt5AqqtpSgVv5l7Ku/uSHaPkd2sDmzHsy5Q4bSGxJQokStk1kidrwle+mbc=,iv:I/Aad82L/TCxStM8d8IZICUrwdjRbGx2fuGWqexr21o=,tag:BfgRbGUxhPZzK2fLik1kxA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
18650
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/peer1
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:vp0yW0Gt,iv:FO2cy+UpEl5aRay/LUGu//c82QiVxuKuGSaVh0rGJvc=,tag:vf2RAOPpcRW0HwxHoGy17A==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjaFVNMEd2YUxpSm5XVVRi\nY2ZUc3NTOStJUFNMWWVPQTgxZ2tCK1QrMW1ZCjYwMlA4dkIzSlc0TGtvZjcyK3Bi\nM3pob2JOOFUyeVJ6M2JpaTRCZlc1R0kKLS0tIDJMb1dFcVRWckhwYWNCQng0RlFO\nTkw3OGt4dkFIZVY5aVEzZE5mMzJSM0EKUv8bUqg48L2FfYVUVlpXvyZvPye699of\nG6PcjLh1ZMbNCfnsCzr+P8Vdk/F4J/ifxL66lRGfu2xOLxwciwQ+5Q==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnZ2dDbVhoQngxM3lTSmZF\nUTAwS1lCTGhEMU1GVXpFUzlIUFdqZy9LajF3Ck9mdVpBRjlyVUNhZXZIUFZjUzF1\nNlhFN28vNmwzcUVkNmlzUnpkWjJuZE0KLS0tIHpXVHVlNk9vU1ZPTGRrYStWbmRO\nbDM4U2o1SlEwYWtqOXBqd3BFUTAvMHcKkI8UVd0v+x+ELZ5CoGq9DzlA6DnVNU2r\nrV9wLfbFd7RHxS0/TYZh5tmU42nO3iMYA9FqERQXCtZgXS9KvfqHwQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-04T12:44:18Z",
|
||||
"mac": "ENC[AES256_GCM,data:1ZZ+ZI1JsHmxTov1bRijfol3kTkyheg2o3ivLsMHRhCmScsUry97hQJchF78+y2Izt7avaQEHYn6pVbYt/0rLrSYD7Ru7ITVxXoYHOiN5Qb98masUzpibZjrdyg5nO+LW5/Hmmwsc3yn/+o3IH1AUYpsxlJRdnHHCmoSOFaiFFM=,iv:OQlgmpOTw4ljujNzqwQ5/0Mz8pQpCSUtqRvj3FJAxDs=,tag:foZvdeW7gK9ZVKkWqnlxGA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
6745
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/machines/peer1
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
not-a-secret
|
||||
@@ -1,27 +1,18 @@
|
||||
{ self, lib, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
filter
|
||||
pathExists
|
||||
;
|
||||
in
|
||||
{ self, ... }:
|
||||
{
|
||||
imports = filter pathExists [
|
||||
imports = [
|
||||
./backups/flake-module.nix
|
||||
./devshell/flake-module.nix
|
||||
./flash/flake-module.nix
|
||||
./impure/flake-module.nix
|
||||
./installation/flake-module.nix
|
||||
./morph/flake-module.nix
|
||||
./nixos-documentation/flake-module.nix
|
||||
./dont-depend-on-repo-root.nix
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
self',
|
||||
system,
|
||||
...
|
||||
}:
|
||||
{
|
||||
@@ -29,91 +20,36 @@ in
|
||||
let
|
||||
nixosTestArgs = {
|
||||
# reference to nixpkgs for the current system
|
||||
inherit pkgs lib;
|
||||
inherit pkgs;
|
||||
# this gives us a reference to our flake but also all flake inputs
|
||||
inherit self;
|
||||
inherit (self) clanLib;
|
||||
};
|
||||
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;
|
||||
|
||||
# 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
|
||||
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"
|
||||
];
|
||||
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
# import our test
|
||||
secrets = import ./secrets nixosTestArgs;
|
||||
container = import ./container nixosTestArgs;
|
||||
# Deltachat is currently marked as broken
|
||||
# deltachat = import ./deltachat nixosTestArgs;
|
||||
borgbackup = import ./borgbackup nixosTestArgs;
|
||||
matrix-synapse = import ./matrix-synapse nixosTestArgs;
|
||||
mumble = import ./mumble nixosTestArgs;
|
||||
syncthing = import ./syncthing nixosTestArgs;
|
||||
zt-tcp-relay = import ./zt-tcp-relay nixosTestArgs;
|
||||
postgresql = import ./postgresql nixosTestArgs;
|
||||
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
|
||||
};
|
||||
|
||||
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}") packagesToBuild
|
||||
) self.nixosConfigurations
|
||||
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
|
||||
// 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 { }
|
||||
);
|
||||
in
|
||||
nixosTests
|
||||
// flakeOutputs
|
||||
// {
|
||||
# TODO: Automatically provide this check to downstream users to check their modules
|
||||
clan-modules-json-compatible =
|
||||
let
|
||||
allSchemas = lib.mapAttrs (
|
||||
_n: m:
|
||||
let
|
||||
schema =
|
||||
(self.clanLib.inventory.evalClanService {
|
||||
modules = [ m ];
|
||||
prefix = [
|
||||
"checks"
|
||||
system
|
||||
];
|
||||
}).config.result.api.schema;
|
||||
in
|
||||
schema
|
||||
) self.clan.modules;
|
||||
in
|
||||
pkgs.runCommand "combined-result"
|
||||
{
|
||||
schemaFile = builtins.toFile "schemas.json" (builtins.toJSON allSchemas);
|
||||
}
|
||||
''
|
||||
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
|
||||
'';
|
||||
};
|
||||
nixosTests // flakeOutputs;
|
||||
legacyPackages = {
|
||||
nixosTests =
|
||||
let
|
||||
@@ -128,8 +64,6 @@ in
|
||||
# import our test
|
||||
secrets = import ./secrets nixosTestArgs;
|
||||
container = import ./container nixosTestArgs;
|
||||
# Clan app tests
|
||||
app-ocr = self.clanLib.test.baseTest ./app-ocr nixosTestArgs;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,41 +1,8 @@
|
||||
{ self, ... }:
|
||||
{
|
||||
config,
|
||||
self,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
clan.machines = lib.listToAttrs (
|
||||
lib.map (
|
||||
system:
|
||||
lib.nameValuePair "test-flash-machine-${system}" {
|
||||
clan.core.networking.targetHost = "test-flash-machine";
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
# We need to use `mkForce` because we inherit from `test-install-machine`
|
||||
# which currently hardcodes `nixpkgs.hostPlatform`
|
||||
nixpkgs.hostPlatform = lib.mkForce system;
|
||||
|
||||
imports = [ self.nixosModules.test-flash-machine ];
|
||||
}
|
||||
) (lib.filter (lib.hasSuffix "linux") config.systems)
|
||||
);
|
||||
|
||||
flake.nixosModules = {
|
||||
test-flash-machine =
|
||||
{ lib, ... }:
|
||||
{
|
||||
imports = [ self.nixosModules.test-install-machine-without-system ];
|
||||
|
||||
clan.core.vars.generators.test = lib.mkForce { };
|
||||
|
||||
disko.devices.disk.main.preCreateHook = lib.mkForce "";
|
||||
};
|
||||
};
|
||||
|
||||
perSystem =
|
||||
{
|
||||
nodes,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
@@ -43,21 +10,17 @@
|
||||
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
|
||||
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.clan.deployment.file
|
||||
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine.config.system.build.toplevel
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine.config.system.build.diskoScript
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine.config.system.build.diskoScript.drvPath
|
||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine.config.system.clan.deployment.file
|
||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
in
|
||||
{
|
||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
||||
flash = self.clanLib.test.baseTest {
|
||||
# Currently disabled...
|
||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
||||
flash = (import ../lib/test-base.nix) {
|
||||
name = "flash";
|
||||
nodes.target = {
|
||||
virtualisation.emptyDiskImages = [ 4096 ];
|
||||
@@ -79,9 +42,7 @@
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
# 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 ${self.checks.x86_64-linux.clan-core-for-checks} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
|
||||
machine.succeed("clan flash write --debug --flake ${../..} --yes --disk main /dev/vdb test-install-machine")
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
[
|
||||
pkgs.gitMinimal
|
||||
pkgs.nix
|
||||
pkgs.coreutils
|
||||
pkgs.rsync # needed to have rsync installed on the dummy ssh server
|
||||
]
|
||||
++ self'.packages.clan-cli-full.runtimeDependencies
|
||||
@@ -31,12 +30,7 @@
|
||||
# this disables dynamic dependency loading in clan-cli
|
||||
export CLAN_NO_DYNAMIC_DEPS=1
|
||||
|
||||
jobs=$(nproc)
|
||||
# Spawning worker in pytest is relatively slow, so we limit the number of jobs to 13
|
||||
# (current number of impure tests)
|
||||
jobs="$((jobs > 13 ? 13 : jobs))"
|
||||
|
||||
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -n $jobs -m impure ./clan_cli $@"
|
||||
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -m impure ./tests $@"
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,234 +1,145 @@
|
||||
{
|
||||
self,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
installer =
|
||||
{ modulesPath, pkgs, ... }:
|
||||
{
|
||||
clan.machines.test-install-machine = {
|
||||
clan.core.networking.targetHost = "test-install-machine";
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
imports = [ self.nixosModules.test-install-machine ];
|
||||
};
|
||||
flake.nixosModules = {
|
||||
test-install-machine =
|
||||
{ lib, modulesPath, ... }:
|
||||
{
|
||||
imports = [
|
||||
self.clanModules.single-disk
|
||||
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
../lib/minify.nix
|
||||
];
|
||||
clan.single-disk.device = "/dev/vda";
|
||||
|
||||
environment.etc."install-successful".text = "ok";
|
||||
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
boot.consoleLogLevel = lib.mkForce 100;
|
||||
boot.kernelParams = [ "boot.shell_on_fail" ];
|
||||
};
|
||||
};
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
dependencies = [
|
||||
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
|
||||
self
|
||||
self.nixosConfigurations.test-install-machine.config.system.build.toplevel
|
||||
self.nixosConfigurations.test-install-machine.config.system.build.diskoScript
|
||||
self.nixosConfigurations.test-install-machine.config.system.clan.deployment.file
|
||||
pkgs.stdenv.drvPath
|
||||
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
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/../tests/common/auto-format-root-device.nix")
|
||||
];
|
||||
networking.useNetworkd = true;
|
||||
services.openssh.enable = true;
|
||||
services.openssh.settings.UseDns = false;
|
||||
services.openssh.settings.PasswordAuthentication = false;
|
||||
system.nixos.variant_id = "installer";
|
||||
environment.systemPackages = [
|
||||
self.packages.${pkgs.system}.clan-cli-full
|
||||
pkgs.nixos-facter
|
||||
];
|
||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||
virtualisation.emptyDiskImages = [ 512 ];
|
||||
virtualisation.diskSize = 8 * 1024;
|
||||
virtualisation.rootDevice = "/dev/vdb";
|
||||
# both installer and target need to use the same diskImage
|
||||
virtualisation.diskImage = "./target.qcow2";
|
||||
virtualisation.memorySize = 3048;
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = lib.mkForce 3;
|
||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
users.users.nonrootuser = {
|
||||
isNormalUser = true;
|
||||
openssh.authorizedKeys.keyFiles = [ ../assets/ssh/pubkey ];
|
||||
extraGroups = [ "wheel" ];
|
||||
};
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
system.extraDependencies = dependencies;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
# The purpose of this test is to ensure `clan machines install` works
|
||||
# for machines that don't have a hardware config yet.
|
||||
|
||||
# If this test starts failing it could be due to the `facter.json` being out of date
|
||||
# you can get a new one by adding
|
||||
# client.fail("cat test-flake/machines/test-install-machine/facter.json >&2")
|
||||
# to the installation test.
|
||||
clan.machines.test-install-machine-without-system = {
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
imports = [ self.nixosModules.test-install-machine-without-system ];
|
||||
};
|
||||
clan.machines.test-install-machine-with-system =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# https://git.clan.lol/clan/test-fixtures
|
||||
facter.reportPath = builtins.fetchurl {
|
||||
url = "https://git.clan.lol/clan/test-fixtures/raw/commit/4a2bc56d886578124b05060d3fb7eddc38c019f8/nixos-vm-facter-json/${pkgs.hostPlatform.system}.json";
|
||||
sha256 =
|
||||
{
|
||||
aarch64-linux = "sha256:1rlfymk03rmfkm2qgrc8l5kj5i20srx79n1y1h4nzlpwaz0j7hh2";
|
||||
x86_64-linux = "sha256:16myh0ll2gdwsiwkjw5ba4dl23ppwbsanxx214863j7nvzx42pws";
|
||||
}
|
||||
.${pkgs.hostPlatform.system};
|
||||
};
|
||||
|
||||
fileSystems."/".device = lib.mkDefault "/dev/vda";
|
||||
boot.loader.grub.device = lib.mkDefault "/dev/vda";
|
||||
|
||||
imports = [ self.nixosModules.test-install-machine-without-system ];
|
||||
};
|
||||
flake.nixosModules = {
|
||||
test-install-machine-without-system =
|
||||
{ lib, modulesPath, ... }:
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
self.clanLib.test.minifyModule
|
||||
];
|
||||
|
||||
networking.hostName = "test-install-machine";
|
||||
|
||||
environment.etc."install-successful".text = "ok";
|
||||
|
||||
boot.consoleLogLevel = lib.mkForce 100;
|
||||
boot.kernelParams = [ "boot.shell_on_fail" ];
|
||||
|
||||
# disko config
|
||||
boot.loader.grub.efiSupport = lib.mkDefault true;
|
||||
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
|
||||
clan.core.vars.settings.secretStore = "vm";
|
||||
clan.core.vars.generators.test = {
|
||||
files.test.neededFor = "partitioning";
|
||||
script = ''
|
||||
echo "notok" > "$out"/test
|
||||
'';
|
||||
};
|
||||
disko.devices = {
|
||||
disk = {
|
||||
main = {
|
||||
type = "disk";
|
||||
device = "/dev/vda";
|
||||
|
||||
preCreateHook = ''
|
||||
test -e /run/partitioning-secrets/test/test
|
||||
'';
|
||||
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = {
|
||||
size = "1M";
|
||||
type = "EF02"; # for grub MBR
|
||||
priority = 1;
|
||||
};
|
||||
ESP = {
|
||||
size = "512M";
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
};
|
||||
};
|
||||
root = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "ext4";
|
||||
mountpoint = "/";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# On aarch64-linux, hangs on reboot with after installation:
|
||||
# vm-test-run-test-installation-> installer # [ 288.002871] reboot: Restarting system
|
||||
# vm-test-run-test-installation-> server # [test-install-machine] ### Done! ###
|
||||
# vm-test-run-test-installation-> server # [test-install-machine] + step 'Done!'
|
||||
# vm-test-run-test-installation-> server # [test-install-machine] + echo '### Done! ###'
|
||||
# vm-test-run-test-installation-> server # [test-install-machine] + rm -rf /tmp/tmp.qb16EAq7hJ
|
||||
# vm-test-run-test-installation-> (finished: must succeed: clan machines install --debug --flake test-flake --yes test-install-machine --target-host root@installer --update-hardware-config nixos-facter >&2, in 154.62 seconds)
|
||||
# vm-test-run-test-installation-> target: starting vm
|
||||
# vm-test-run-test-installation-> target: QEMU running (pid 144)
|
||||
# vm-test-run-test-installation-> target: waiting for unit multi-user.target
|
||||
# vm-test-run-test-installation-> target: waiting for the VM to finish booting
|
||||
# vm-test-run-test-installation-> target: Guest root shell did not produce any data yet...
|
||||
# vm-test-run-test-installation-> target: To debug, enter the VM and run 'systemctl status backdoor.service'.
|
||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) {
|
||||
installation = self.clanLib.test.baseTest {
|
||||
name = "installation";
|
||||
# vm-test-run-test-installation> (finished: waiting for the VM to power off, in 1.97 seconds)
|
||||
# vm-test-run-test-installation>
|
||||
# vm-test-run-test-installation> new_machine: must succeed: cat /etc/install-successful
|
||||
# vm-test-run-test-installation> new_machine: waiting for the VM to finish booting
|
||||
# vm-test-run-test-installation> new_machine: starting vm
|
||||
# vm-test-run-test-installation> new_machine: QEMU running (pid 80)
|
||||
# vm-test-run-test-installation> new_machine: Guest root shell did not produce any data yet...
|
||||
# vm-test-run-test-installation> new_machine: To debug, enter the VM and run 'systemctl status backdoor.service'.
|
||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux && pkgs.stdenv.hostPlatform.system != "aarch64-linux") {
|
||||
test-installation = (import ../lib/test-base.nix) {
|
||||
name = "test-installation";
|
||||
nodes.target = {
|
||||
services.openssh.enable = true;
|
||||
virtualisation.diskImage = "./target.qcow2";
|
||||
virtualisation.useBootLoader = true;
|
||||
|
||||
# virtualisation.fileSystems."/" = {
|
||||
# device = "/dev/disk/by-label/this-is-not-real-and-will-never-be-used";
|
||||
# fsType = "ext4";
|
||||
# };
|
||||
};
|
||||
nodes.installer =
|
||||
{ modulesPath, ... }:
|
||||
{
|
||||
imports = [
|
||||
(modulesPath + "/../tests/common/auto-format-root-device.nix")
|
||||
];
|
||||
services.openssh.enable = true;
|
||||
users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
|
||||
system.nixos.variant_id = "installer";
|
||||
environment.systemPackages = [ pkgs.nixos-facter ];
|
||||
virtualisation.emptyDiskImages = [ 512 ];
|
||||
virtualisation.diskSize = 8 * 1024;
|
||||
virtualisation.rootDevice = "/dev/vdb";
|
||||
# both installer and target need to use the same diskImage
|
||||
virtualisation.diskImage = "./target.qcow2";
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = lib.mkForce 3;
|
||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
system.extraDependencies = dependencies;
|
||||
};
|
||||
nodes.client = {
|
||||
environment.systemPackages = [
|
||||
self.packages.${pkgs.system}.clan-cli
|
||||
] ++ self.packages.${pkgs.system}.clan-cli.runtimeDependencies;
|
||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||
virtualisation.memorySize = 2048;
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
hashed-mirrors = null;
|
||||
connect-timeout = lib.mkForce 3;
|
||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
system.extraDependencies = dependencies;
|
||||
};
|
||||
nodes.installer = installer;
|
||||
|
||||
testScript = ''
|
||||
client.start()
|
||||
installer.start()
|
||||
|
||||
installer.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../assets/ssh/privkey} /root/.ssh/id_ed25519")
|
||||
client.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../lib/ssh/privkey} /root/.ssh/id_ed25519")
|
||||
client.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new -v root@installer hostname")
|
||||
client.succeed("cp -r ${../..} test-flake && chmod -R +w test-flake")
|
||||
client.fail("test -f test-flake/machines/test-install-machine/hardware-configuration.nix")
|
||||
client.succeed("clan machines update-hardware-config --flake test-flake test-install-machine root@installer >&2")
|
||||
client.succeed("test -f test-flake/machines/test-install-machine/hardware-configuration.nix")
|
||||
client.succeed("clan machines update-hardware-config --backend nixos-facter --flake test-flake test-install-machine root@installer>&2")
|
||||
client.succeed("test -f test-flake/machines/test-install-machine/facter.json")
|
||||
client.succeed("clan machines install --debug --flake ${../..} --yes test-install-machine --target-host root@installer >&2")
|
||||
try:
|
||||
installer.shutdown()
|
||||
except BrokenPipeError:
|
||||
# qemu has already exited
|
||||
pass
|
||||
|
||||
installer.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new -v nonrootuser@localhost hostname")
|
||||
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()
|
||||
|
||||
# We are missing the test instrumentation somehow. Test this later.
|
||||
target.state_dir = installer.state_dir
|
||||
target.start()
|
||||
target.wait_for_unit("multi-user.target")
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
|
||||
update-hardware-configuration = self.clanLib.test.baseTest {
|
||||
name = "update-hardware-configuration";
|
||||
nodes.installer = installer;
|
||||
|
||||
testScript = ''
|
||||
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 ${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")
|
||||
|
||||
installer.succeed("clan machines update-hardware-config --debug --flake test-flake test-install-machine-without-system nonrootuser@localhost >&2")
|
||||
installer.succeed("test -f test-flake/machines/test-install-machine-without-system/facter.json")
|
||||
installer.succeed("rm test-flake/machines/test-install-machine-without-system/facter.json")
|
||||
|
||||
installer.succeed("clan machines update-hardware-config --debug --backend nixos-generate-config --flake test-flake test-install-machine-without-system nonrootuser@localhost >&2")
|
||||
installer.succeed("test -f test-flake/machines/test-install-machine-without-system/hardware-configuration.nix")
|
||||
installer.succeed("rm test-flake/machines/test-install-machine-without-system/hardware-configuration.nix")
|
||||
assert(target.succeed("cat /etc/install-successful").strip() == "ok")
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
};
|
||||
|
||||
@@ -7,19 +7,9 @@
|
||||
let
|
||||
testDriver = hostPkgs.python3.pkgs.callPackage ./package.nix {
|
||||
inherit (config) extraPythonPackages;
|
||||
inherit (hostPkgs.pkgs) util-linux systemd nix;
|
||||
inherit (hostPkgs.pkgs) util-linux systemd;
|
||||
};
|
||||
containers =
|
||||
testScript:
|
||||
map (m: [
|
||||
m.system.build.toplevel
|
||||
(hostPkgs.closureInfo {
|
||||
rootPaths = [
|
||||
m.system.build.toplevel
|
||||
(hostPkgs.writeText "testScript" testScript)
|
||||
];
|
||||
})
|
||||
]) (lib.attrValues config.nodes);
|
||||
containers = map (m: m.system.build.toplevel) (lib.attrValues config.nodes);
|
||||
pythonizeName =
|
||||
name:
|
||||
let
|
||||
@@ -37,9 +27,6 @@ let
|
||||
pythonizedNames = map pythonizeName nodeHostNames;
|
||||
in
|
||||
{
|
||||
defaults.imports = [
|
||||
./nixos-module.nix
|
||||
];
|
||||
driver = lib.mkForce (
|
||||
hostPkgs.runCommand "nixos-test-driver-${config.name}"
|
||||
{
|
||||
@@ -57,6 +44,8 @@ in
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
|
||||
containers=(${toString containers})
|
||||
|
||||
${lib.optionalString (!config.skipTypeCheck) ''
|
||||
# prepend type hints so the test script can be type checked with mypy
|
||||
cat "${./test-script-prepend.py}" >> testScriptWithTypes
|
||||
@@ -77,13 +66,7 @@ in
|
||||
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
||||
|
||||
wrapProgram $out/bin/nixos-test-driver \
|
||||
${
|
||||
lib.concatStringsSep " " (
|
||||
map (container: "--add-flags '--container ${builtins.toString container}'") (
|
||||
containers config.testScriptString
|
||||
)
|
||||
)
|
||||
} \
|
||||
${lib.concatStringsSep " " (map (name: "--add-flags '--container ${name}'") containers)} \
|
||||
--add-flags "--test-script '$out/test-script'"
|
||||
''
|
||||
);
|
||||
@@ -92,7 +75,7 @@ in
|
||||
lib.lazyDerivation {
|
||||
# lazyDerivation improves performance when only passthru items and/or meta are used.
|
||||
derivation = hostPkgs.stdenv.mkDerivation {
|
||||
name = "container-test-run-${config.name}";
|
||||
name = "vm-test-run-${config.name}";
|
||||
|
||||
requiredSystemFeatures = [ "uid-range" ];
|
||||
|
||||
@@ -105,12 +88,6 @@ in
|
||||
${config.driver}/bin/nixos-test-driver -o $out
|
||||
'';
|
||||
|
||||
nativeBuildInputs = [
|
||||
hostPkgs.util-linux
|
||||
hostPkgs.coreutils
|
||||
hostPkgs.iproute2
|
||||
];
|
||||
|
||||
passthru = config.passthru;
|
||||
|
||||
meta = config.meta;
|
||||
23
checks/lib/container-driver/package.nix
Normal file
23
checks/lib/container-driver/package.nix
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
extraPythonPackages,
|
||||
python3Packages,
|
||||
buildPythonApplication,
|
||||
setuptools,
|
||||
util-linux,
|
||||
systemd,
|
||||
colorama,
|
||||
junit-xml,
|
||||
}:
|
||||
buildPythonApplication {
|
||||
pname = "test-driver";
|
||||
version = "0.0.1";
|
||||
propagatedBuildInputs = [
|
||||
util-linux
|
||||
systemd
|
||||
colorama
|
||||
junit-xml
|
||||
] ++ extraPythonPackages python3Packages;
|
||||
nativeBuildInputs = [ setuptools ];
|
||||
format = "pyproject";
|
||||
src = ./.;
|
||||
}
|
||||
@@ -1,73 +1,17 @@
|
||||
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
|
||||
from functools import cached_property
|
||||
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
|
||||
libc = ctypes.CDLL("libc.so.6", use_errno=True)
|
||||
|
||||
# Define the mount function
|
||||
libc.mount.argtypes = [
|
||||
ctypes.c_char_p, # source
|
||||
ctypes.c_char_p, # target
|
||||
ctypes.c_char_p, # filesystemtype
|
||||
ctypes.c_ulong, # mountflags
|
||||
ctypes.c_void_p, # data
|
||||
]
|
||||
libc.mount.restype = ctypes.c_int
|
||||
|
||||
MS_BIND = 0x1000
|
||||
MS_REC = 0x4000
|
||||
|
||||
|
||||
def mount(
|
||||
source: Path,
|
||||
target: Path,
|
||||
filesystemtype: str,
|
||||
mountflags: int = 0,
|
||||
data: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
A Python wrapper for the mount system call.
|
||||
|
||||
:param source: The source of the file system (e.g., device name, remote filesystem).
|
||||
:param target: The mount point (an existing directory).
|
||||
:param filesystemtype: The filesystem type (e.g., "ext4", "nfs").
|
||||
:param mountflags: Mount options flags.
|
||||
:param data: File system-specific data (e.g., options like "rw").
|
||||
:raises OSError: If the mount system call fails.
|
||||
"""
|
||||
# Convert Python strings to C-compatible strings
|
||||
source_c = ctypes.c_char_p(str(source).encode("utf-8"))
|
||||
target_c = ctypes.c_char_p(str(target).encode("utf-8"))
|
||||
fstype_c = ctypes.c_char_p(filesystemtype.encode("utf-8"))
|
||||
data_c = ctypes.c_char_p(data.encode("utf-8")) if data else None
|
||||
|
||||
# Call the mount system call
|
||||
result = libc.mount(
|
||||
source_c, target_c, fstype_c, ctypes.c_ulong(mountflags), data_c
|
||||
)
|
||||
|
||||
if result != 0:
|
||||
errno = ctypes.get_errno()
|
||||
raise OSError(errno, os.strerror(errno))
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
@@ -116,10 +60,6 @@ class Machine:
|
||||
self.rootdir: Path = rootdir
|
||||
self.logger = logger
|
||||
|
||||
@cached_property
|
||||
def container_pid(self) -> int:
|
||||
return self.get_systemd_process()
|
||||
|
||||
def start(self) -> None:
|
||||
prepare_machine_root(self.name, self.rootdir)
|
||||
cmd = [
|
||||
@@ -131,16 +71,18 @@ class Machine:
|
||||
self.rootdir,
|
||||
"--register=no",
|
||||
"--resolv-conf=off",
|
||||
f"--bind=/.containers/{self.name}/nix:/nix",
|
||||
"--bind-ro=/nix/store",
|
||||
"--bind",
|
||||
self.out_dir,
|
||||
"--bind=/proc:/run/host/proc",
|
||||
"--bind=/sys:/run/host/sys",
|
||||
"--private-network",
|
||||
"--network-bridge=br0",
|
||||
self.toplevel.joinpath("init"),
|
||||
]
|
||||
env = os.environ.copy()
|
||||
env["SYSTEMD_NSPAWN_UNIFIED_HIERARCHY"] = "1"
|
||||
self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True, env=env)
|
||||
self.container_pid = self.get_systemd_process()
|
||||
|
||||
def get_systemd_process(self) -> int:
|
||||
assert self.process is not None, "Machine not started"
|
||||
@@ -160,9 +102,9 @@ class Machine:
|
||||
.read_text()
|
||||
.split()
|
||||
)
|
||||
assert len(childs) == 1, (
|
||||
f"Expected exactly one child process for systemd-nspawn, got {childs}"
|
||||
)
|
||||
assert (
|
||||
len(childs) == 1
|
||||
), f"Expected exactly one child process for systemd-nspawn, got {childs}"
|
||||
try:
|
||||
return int(childs[0])
|
||||
except ValueError as e:
|
||||
@@ -191,22 +133,6 @@ 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,
|
||||
@@ -248,10 +174,23 @@ class Machine:
|
||||
"""
|
||||
|
||||
# Always run command with shell opts
|
||||
command = f"set -eo pipefail; source /etc/profile; set -xu; {command}"
|
||||
command = f"set -eo pipefail; source /etc/profile; set -u; {command}"
|
||||
|
||||
proc = subprocess.run(
|
||||
self.nsenter_command(command),
|
||||
[
|
||||
"nsenter",
|
||||
"--target",
|
||||
str(self.container_pid),
|
||||
"--mount",
|
||||
"--uts",
|
||||
"--ipc",
|
||||
"--net",
|
||||
"--pid",
|
||||
"--cgroup",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
command,
|
||||
],
|
||||
timeout=timeout,
|
||||
check=False,
|
||||
stdout=subprocess.PIPE,
|
||||
@@ -314,9 +253,7 @@ class Machine:
|
||||
info = self.get_unit_info(unit)
|
||||
state = info["ActiveState"]
|
||||
if state == "failed":
|
||||
proc = self.systemctl(f"--lines 0 status {unit}")
|
||||
journal = self.execute(f"journalctl -u {unit} --no-pager")
|
||||
msg = f'unit "{unit}" reached state "{state}":\n{proc.stdout}\n{journal.stdout}'
|
||||
msg = f'unit "{unit}" reached state "{state}"'
|
||||
raise Error(msg)
|
||||
|
||||
if state == "inactive":
|
||||
@@ -334,18 +271,7 @@ class Machine:
|
||||
def succeed(self, command: str, timeout: int | None = None) -> str:
|
||||
res = self.execute(command, timeout=timeout)
|
||||
if res.returncode != 0:
|
||||
msg = f"Failed to run command {command}\n"
|
||||
msg += f"Exit code: {res.returncode}\n"
|
||||
msg += f"Stdout: {res.stdout}"
|
||||
raise RuntimeError(msg)
|
||||
return res.stdout
|
||||
|
||||
def fail(self, command: str, timeout: int | None = None) -> str:
|
||||
res = self.execute(command, timeout=timeout)
|
||||
if res.returncode == 0:
|
||||
msg = f"command `{command}` unexpectedly succeeded\n"
|
||||
msg += f"Exit code: {res.returncode}\n"
|
||||
msg += f"Stdout: {res.stdout}"
|
||||
msg = f"Failed to run command {command}"
|
||||
raise RuntimeError(msg)
|
||||
return res.stdout
|
||||
|
||||
@@ -362,70 +288,14 @@ class Machine:
|
||||
self.shutdown()
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContainerInfo:
|
||||
toplevel: Path
|
||||
closure_info: Path
|
||||
|
||||
@cached_property
|
||||
def name(self) -> str:
|
||||
name_match = re.match(r".*-nixos-system-(.+)-(.+)", self.toplevel.name)
|
||||
if not name_match:
|
||||
msg = f"Unable to extract hostname from {self.toplevel.name}"
|
||||
raise Error(msg)
|
||||
return name_match.group(1)
|
||||
|
||||
@property
|
||||
def root_dir(self) -> Path:
|
||||
return Path(f"/.containers/{self.name}")
|
||||
|
||||
@property
|
||||
def nix_store_dir(self) -> Path:
|
||||
return self.root_dir / "nix" / "store"
|
||||
|
||||
@property
|
||||
def etc_dir(self) -> Path:
|
||||
return self.root_dir / "etc"
|
||||
|
||||
|
||||
def setup_filesystems(container: ContainerInfo) -> None:
|
||||
def setup_filesystems() -> None:
|
||||
# We don't care about cleaning up the mount points, since we're running in a nix sandbox.
|
||||
Path("/run").mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(["mount", "-t", "tmpfs", "none", "/run"], check=True)
|
||||
subprocess.run(["mount", "-t", "cgroup2", "none", "/sys/fs/cgroup"], check=True)
|
||||
container.etc_dir.mkdir(parents=True)
|
||||
Path("/etc").chmod(0o755)
|
||||
Path("/etc/os-release").touch()
|
||||
Path("/etc/machine-id").write_text("a5ea3f98dedc0278b6f3cc8c37eeaeac")
|
||||
container.nix_store_dir.mkdir(parents=True)
|
||||
# Read /proc/mounts and replicate every bind mount
|
||||
with Path("/proc/self/mounts").open() as f:
|
||||
for line in f:
|
||||
columns = line.split(" ")
|
||||
source = Path(columns[1])
|
||||
if source.parent != Path("/nix/store/"):
|
||||
continue
|
||||
target = container.nix_store_dir / source.name
|
||||
if source.is_dir():
|
||||
target.mkdir()
|
||||
else:
|
||||
target.touch()
|
||||
try:
|
||||
if "acl" in target.name:
|
||||
print(f"mount({source}, {target})")
|
||||
mount(source, target, "none", MS_BIND)
|
||||
except OSError as e:
|
||||
msg = f"mount({source}, {target}) failed"
|
||||
raise Error(msg) from e
|
||||
|
||||
|
||||
def load_nix_db(container: ContainerInfo) -> None:
|
||||
with (container.closure_info / "registration").open() as f:
|
||||
subprocess.run(
|
||||
["nix-store", "--load-db", "--store", str(container.root_dir)],
|
||||
stdin=f,
|
||||
check=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
class Driver:
|
||||
@@ -433,7 +303,7 @@ class Driver:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
containers: list[ContainerInfo],
|
||||
containers: list[Path],
|
||||
logger: AbstractLogger,
|
||||
testscript: str,
|
||||
out_dir: str,
|
||||
@@ -442,73 +312,32 @@ class Driver:
|
||||
self.testscript = testscript
|
||||
self.out_dir = out_dir
|
||||
self.logger = logger
|
||||
setup_filesystems()
|
||||
|
||||
self.tempdir = TemporaryDirectory()
|
||||
tempdir_path = Path(self.tempdir.name)
|
||||
|
||||
self.machines = []
|
||||
for container in containers:
|
||||
setup_filesystems(container)
|
||||
load_nix_db(container)
|
||||
name_match = re.match(r".*-nixos-system-(.+)-(.+)", container.name)
|
||||
if not name_match:
|
||||
msg = f"Unable to extract hostname from {container.name}"
|
||||
raise Error(msg)
|
||||
name = name_match.group(1)
|
||||
self.machines.append(
|
||||
Machine(
|
||||
name=container.name,
|
||||
toplevel=container.toplevel,
|
||||
rootdir=tempdir_path / container.name,
|
||||
name=name,
|
||||
toplevel=container,
|
||||
rootdir=tempdir_path / name,
|
||||
out_dir=self.out_dir,
|
||||
logger=self.logger,
|
||||
)
|
||||
)
|
||||
|
||||
def start_all(self) -> None:
|
||||
# child
|
||||
# create bridge
|
||||
subprocess.run(
|
||||
["ip", "link", "add", "br0", "type", "bridge"], check=True, text=True
|
||||
)
|
||||
subprocess.run(["ip", "link", "set", "br0", "up"], check=True, text=True)
|
||||
|
||||
for machine in self.machines:
|
||||
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,
|
||||
@@ -570,11 +399,9 @@ def main() -> None:
|
||||
arg_parser = argparse.ArgumentParser(prog="nixos-test-driver")
|
||||
arg_parser.add_argument(
|
||||
"--containers",
|
||||
nargs=2,
|
||||
action="append",
|
||||
nargs="+",
|
||||
type=Path,
|
||||
metavar=("TOPLEVEL_STORE_DIR", "CLOSURE_INFO"),
|
||||
help="container system toplevel store dir and closure info",
|
||||
help="container system toplevel paths",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--test-script",
|
||||
@@ -591,10 +418,7 @@ def main() -> None:
|
||||
args = arg_parser.parse_args()
|
||||
logger = CompositeLogger([TerminalLogger()])
|
||||
with Driver(
|
||||
containers=[
|
||||
ContainerInfo(toplevel, closure_info)
|
||||
for toplevel, closure_info in args.containers
|
||||
],
|
||||
containers=args.containers,
|
||||
testscript=args.test_script.read_text(),
|
||||
out_dir=args.output_directory.resolve(),
|
||||
logger=logger,
|
||||
36
checks/lib/container-test.nix
Normal file
36
checks/lib/container-test.nix
Normal file
@@ -0,0 +1,36 @@
|
||||
test:
|
||||
{ pkgs, self, ... }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
||||
in
|
||||
(nixos-lib.runTest (
|
||||
{ hostPkgs, ... }:
|
||||
{
|
||||
hostPkgs = pkgs;
|
||||
# speed-up evaluation
|
||||
defaults = {
|
||||
imports = [
|
||||
./minify.nix
|
||||
];
|
||||
documentation.enable = lib.mkDefault false;
|
||||
boot.isContainer = true;
|
||||
|
||||
# undo qemu stuff
|
||||
system.build.initialRamdisk = "";
|
||||
virtualisation.sharedDirectories = lib.mkForce { };
|
||||
networking.useDHCP = false;
|
||||
|
||||
# we have not private networking so far
|
||||
networking.interfaces = lib.mkForce { };
|
||||
#networking.primaryIPAddress = lib.mkForce null;
|
||||
systemd.services.backdoor.enable = false;
|
||||
};
|
||||
# to accept external dependencies such as disko
|
||||
node.specialArgs.self = self;
|
||||
imports = [
|
||||
test
|
||||
./container-driver/module.nix
|
||||
];
|
||||
}
|
||||
)).config.result
|
||||
7
checks/lib/minify.nix
Normal file
7
checks/lib/minify.nix
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
nixpkgs.flake.setFlakeRegistry = false;
|
||||
nixpkgs.flake.setNixPath = false;
|
||||
nix.registry.nixpkgs.to = { };
|
||||
documentation.doc.enable = false;
|
||||
documentation.man.enable = false;
|
||||
}
|
||||
22
checks/lib/test-base.nix
Normal file
22
checks/lib/test-base.nix
Normal file
@@ -0,0 +1,22 @@
|
||||
test:
|
||||
{ pkgs, self, ... }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
||||
in
|
||||
(nixos-lib.runTest {
|
||||
hostPkgs = pkgs;
|
||||
# speed-up evaluation
|
||||
defaults = {
|
||||
imports = [
|
||||
./minify.nix
|
||||
];
|
||||
documentation.enable = lib.mkDefault false;
|
||||
nix.settings.min-free = 0;
|
||||
system.stateVersion = lib.version;
|
||||
};
|
||||
|
||||
# to accept external dependencies such as disko
|
||||
node.specialArgs.self = self;
|
||||
imports = [ test ];
|
||||
}).config.result
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user