Compare commits
2 Commits
pinned-cla
...
options-wa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41635dd350 | ||
|
|
16d16faa9c |
@@ -8,5 +8,5 @@ jobs:
|
|||||||
checks-impure:
|
checks-impure:
|
||||||
runs-on: nix
|
runs-on: nix
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- run: nix run .#impure-checks
|
- run: nix run .#impure-checks
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ jobs:
|
|||||||
deploy-docs:
|
deploy-docs:
|
||||||
runs-on: nix
|
runs-on: nix
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- run: nix run .#deploy-docs
|
- run: nix run .#deploy-docs
|
||||||
env:
|
env:
|
||||||
SSH_HOMEPAGE_KEY: ${{ secrets.SSH_HOMEPAGE_KEY }}
|
SSH_HOMEPAGE_KEY: ${{ secrets.SSH_HOMEPAGE_KEY }}
|
||||||
|
|||||||
@@ -1,29 +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
|
|
||||||
run: |
|
|
||||||
git commit -am ""
|
|
||||||
git push origin HEAD:update-clan-core-for-checks
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"head": "update-clan-core-branch",
|
|
||||||
"base": "main",
|
|
||||||
"title": "Automated Update: Clan Core",
|
|
||||||
"body": "This PR updates the pinned clan-core for checks."
|
|
||||||
}' \
|
|
||||||
"${GITEA_SERVER_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/pulls"
|
|
||||||
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:
|
schedule:
|
||||||
- cron: "39 * * * *"
|
- cron: "39 * * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
repo-sync:
|
repo-sync:
|
||||||
if: github.repository_owner == 'clan-lol'
|
if: github.repository_owner == 'clan-lol'
|
||||||
@@ -13,15 +15,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
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
|
- name: repo-sync
|
||||||
uses: repo-sync/github-sync@v2
|
uses: repo-sync/github-sync@v2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
source_repo: "https://git.clan.lol/clan/clan-core.git"
|
source_repo: "https://git.clan.lol/clan/clan-core.git"
|
||||||
source_branch: "main"
|
source_branch: "main"
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -14,11 +14,7 @@ example_clan
|
|||||||
nixos.qcow2
|
nixos.qcow2
|
||||||
**/*.glade~
|
**/*.glade~
|
||||||
/docs/out
|
/docs/out
|
||||||
/pkgs/clan-cli/clan_cli/select
|
|
||||||
**/.local.env
|
|
||||||
|
|
||||||
# MacOS stuff
|
|
||||||
**/.DS_store
|
|
||||||
|
|
||||||
# dream2nix
|
# dream2nix
|
||||||
.dream2nix
|
.dream2nix
|
||||||
@@ -43,6 +39,3 @@ repo
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
.webui
|
.webui
|
||||||
|
|
||||||
# TODO: remove after bug in select is fixed
|
|
||||||
select
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
nixosModules/clanCore/vars/.* @lopter
|
|
||||||
pkgs/clan-cli/clan_cli/(secrets|vars)/.* @lopter
|
|
||||||
@@ -1,4 +1,23 @@
|
|||||||
# Contributing to Clan
|
# Contributing to Clan
|
||||||
|
|
||||||
<!-- Local file: docs/CONTRIBUTING.md -->
|
## Live-reloading documentation
|
||||||
Go to the Contributing guide at https://docs.clan.lol/manual/contribute/
|
|
||||||
|
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.
|
||||||
|
|||||||
@@ -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";
|
fileSystems."/".device = "/dev/null";
|
||||||
boot.loader.grub.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 = {
|
flake.nixosModules = {
|
||||||
test-backup =
|
test-backup =
|
||||||
{
|
{
|
||||||
@@ -28,30 +22,21 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
# Do not import inventory modules. They should be configured via 'clan.inventory'
|
self.clanModules.borgbackup
|
||||||
#
|
|
||||||
# TODO: Configure localbackup via inventory
|
|
||||||
self.clanModules.localbackup
|
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";
|
clan.core.networking.targetHost = "machine";
|
||||||
networking.hostName = "machine";
|
networking.hostName = "machine";
|
||||||
|
services.openssh.settings.UseDns = false;
|
||||||
|
nixpkgs.hostPlatform = "x86_64-linux";
|
||||||
|
|
||||||
programs.ssh.knownHosts = {
|
programs.ssh.knownHosts = {
|
||||||
machine.hostNames = [ "machine" ];
|
machine.hostNames = [ "machine" ];
|
||||||
machine.publicKey = builtins.readFile ../assets/ssh/pubkey;
|
machine.publicKey = builtins.readFile ../lib/ssh/pubkey;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.UsePAM = false;
|
|
||||||
settings.UseDns = false;
|
|
||||||
hostKeys = [
|
hostKeys = [
|
||||||
{
|
{
|
||||||
path = "/root/.ssh/id_ed25519";
|
path = "/root/.ssh/id_ed25519";
|
||||||
@@ -60,35 +45,31 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.root.openssh.authorizedKeys.keyFiles = [ ../assets/ssh/pubkey ];
|
users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
|
||||||
|
|
||||||
# This is needed to unlock the user for sshd
|
|
||||||
# Because we use sshd without setuid binaries
|
|
||||||
users.users.borg.initialPassword = "hello";
|
|
||||||
|
|
||||||
systemd.tmpfiles.settings."vmsecrets" = {
|
systemd.tmpfiles.settings."vmsecrets" = {
|
||||||
"/root/.ssh/id_ed25519" = {
|
"/root/.ssh/id_ed25519" = {
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
C.argument = "${../lib/ssh/privkey}";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"/etc/secrets/ssh.id_ed25519" = {
|
"/etc/secrets/ssh.id_ed25519" = {
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
C.argument = "${../lib/ssh/privkey}";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"/etc/secrets/borgbackup/borgbackup.ssh" = {
|
"/etc/secrets/borgbackup.ssh" = {
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
C.argument = "${../lib/ssh/privkey}";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"/etc/secrets/borgbackup/borgbackup.repokey" = {
|
"/etc/secrets/borgbackup.repokey" = {
|
||||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
@@ -97,7 +78,8 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
clan.core.facts.secretStore = "vm";
|
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.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||||
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
||||||
@@ -122,6 +104,7 @@
|
|||||||
'';
|
'';
|
||||||
folders = [ "/var/test-service" ];
|
folders = [ "/var/test-service" ];
|
||||||
};
|
};
|
||||||
|
clan.borgbackup.destinations.test-backup.repo = "borg@machine:.";
|
||||||
|
|
||||||
fileSystems."/mnt/external-disk" = {
|
fileSystems."/mnt/external-disk" = {
|
||||||
device = "/dev/vdb"; # created in tests with virtualisation.emptyDisks
|
device = "/dev/vdb"; # created in tests with virtualisation.emptyDisks
|
||||||
@@ -142,27 +125,28 @@
|
|||||||
touch /run/unmount-external-disk
|
touch /run/unmount-external-disk
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.borgbackup.repos.test-backups = {
|
||||||
|
path = "/var/lib/borgbackup/test-backups";
|
||||||
|
authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
perSystem =
|
perSystem =
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
let
|
|
||||||
clanCore = self.checks.x86_64-linux.clan-core-for-checks;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
# Needs investigation on aarch64-linux
|
||||||
backups = self.clanLib.test.containerTest {
|
# vm-test-run-test-backups> qemu-kvm: No machine specified, and there is no default
|
||||||
name = "backups";
|
# 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 = {
|
nodes.machine = {
|
||||||
imports =
|
imports = [
|
||||||
[
|
self.nixosModules.clanCore
|
||||||
self.nixosModules.clanCore
|
self.nixosModules.test-backup
|
||||||
# Some custom overrides for the backup tests
|
];
|
||||||
self.nixosModules.test-backup
|
virtualisation.emptyDiskImages = [ 256 ];
|
||||||
]
|
|
||||||
++
|
|
||||||
# import the inventory generated nixosModules
|
|
||||||
self.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
|
||||||
clan.core.settings.directory = ./.;
|
clan.core.settings.directory = ./.;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,14 +159,14 @@
|
|||||||
machine.succeed("echo testing > /var/test-backups/somefile")
|
machine.succeed("echo testing > /var/test-backups/somefile")
|
||||||
|
|
||||||
# create
|
# 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.wait_until_succeeds("! systemctl is-active borgbackup-job-test-backup >&2")
|
||||||
machine.succeed("test -f /run/mount-external-disk")
|
machine.succeed("test -f /run/mount-external-disk")
|
||||||
machine.succeed("test -f /run/unmount-external-disk")
|
machine.succeed("test -f /run/unmount-external-disk")
|
||||||
|
|
||||||
# list
|
# list
|
||||||
backup_id = json.loads(machine.succeed("borg-job-test-backup list --json"))["archives"][0]["archive"]
|
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)
|
print(out)
|
||||||
assert backup_id in out, f"backup {backup_id} not found in {out}"
|
assert backup_id in out, f"backup {backup_id} not found in {out}"
|
||||||
localbackup_id = "hdd::/mnt/external-disk/snapshot.0"
|
localbackup_id = "hdd::/mnt/external-disk/snapshot.0"
|
||||||
@@ -190,7 +174,7 @@
|
|||||||
|
|
||||||
## borgbackup restore
|
## borgbackup restore
|
||||||
machine.succeed("rm -f /var/test-backups/somefile")
|
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"
|
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/pre-restore-command")
|
||||||
machine.succeed("test -f /var/test-service/post-restore-command")
|
machine.succeed("test -f /var/test-service/post-restore-command")
|
||||||
@@ -198,7 +182,7 @@
|
|||||||
|
|
||||||
## localbackup restore
|
## localbackup restore
|
||||||
machine.succeed("rm -rf /var/test-backups/somefile /var/test-service/ && mkdir -p /var/test-service")
|
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"
|
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/pre-restore-command")
|
||||||
machine.succeed("test -f /var/test-service/post-restore-command")
|
machine.succeed("test -f /var/test-service/post-restore-command")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
(
|
(import ../lib/test-base.nix) (
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
name = "borgbackup";
|
name = "borgbackup";
|
||||||
@@ -12,22 +12,23 @@
|
|||||||
{
|
{
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
services.borgbackup.repos.testrepo = {
|
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.settings.directory = ./.;
|
||||||
clan.core.state.testState.folders = [ "/etc/state" ];
|
clan.core.state.testState.folders = [ "/etc/state" ];
|
||||||
environment.etc.state.text = "hello world";
|
environment.etc.state.text = "hello world";
|
||||||
systemd.tmpfiles.settings."vmsecrets" = {
|
systemd.tmpfiles.settings."vmsecrets" = {
|
||||||
"/etc/secrets/borgbackup/borgbackup.ssh" = {
|
"/etc/secrets/borgbackup.ssh" = {
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
C.argument = "${../lib/ssh/privkey}";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"/etc/secrets/borgbackup/borgbackup.repokey" = {
|
"/etc/secrets/borgbackup.repokey" = {
|
||||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
@@ -35,8 +36,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
# clan.core.facts.secretStore = "vm";
|
clan.core.facts.secretStore = "vm";
|
||||||
clan.core.vars.settings.secretStore = "vm";
|
|
||||||
|
|
||||||
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{ fetchgit }:
|
|
||||||
fetchgit {
|
|
||||||
url = "https://git.clan.lol/clan/clan-core.git";
|
|
||||||
rev = "1e8b9def2a021877342491ca1f4c45533a580759";
|
|
||||||
sha256 = "0f12vwr1abwa1iwjbb5z5xx8jlh80d9njwdm6iaw1z1h2m76xgzc";
|
|
||||||
}
|
|
||||||
@@ -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.enable = true;
|
||||||
services.openssh.startWhenNeeded = false;
|
services.openssh.startWhenNeeded = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
nodes.machine2 =
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
networking.hostName = "machine2";
|
|
||||||
services.openssh.enable = true;
|
|
||||||
services.openssh.startWhenNeeded = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
import subprocess
|
|
||||||
start_all()
|
start_all()
|
||||||
machine1.succeed("systemctl status sshd")
|
machine.succeed("systemctl status sshd")
|
||||||
machine2.succeed("systemctl status sshd")
|
machine.wait_for_unit("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")
|
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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.clanModules.deltachat
|
||||||
self.nixosModules.clanCore
|
self.nixosModules.clanCore
|
||||||
{
|
{
|
||||||
|
clan.core.settings.machine.name = "machine";
|
||||||
clan.core.settings.directory = ./.;
|
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, ... }:
|
{ self, ... }:
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
filter
|
|
||||||
pathExists
|
|
||||||
;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
imports = filter pathExists [
|
imports = [
|
||||||
./backups/flake-module.nix
|
./backups/flake-module.nix
|
||||||
./devshell/flake-module.nix
|
./devshell/flake-module.nix
|
||||||
./flash/flake-module.nix
|
./flash/flake-module.nix
|
||||||
./impure/flake-module.nix
|
./impure/flake-module.nix
|
||||||
./installation/flake-module.nix
|
./installation/flake-module.nix
|
||||||
./morph/flake-module.nix
|
|
||||||
./nixos-documentation/flake-module.nix
|
./nixos-documentation/flake-module.nix
|
||||||
./dont-depend-on-repo-root.nix
|
|
||||||
];
|
];
|
||||||
perSystem =
|
perSystem =
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
self',
|
self',
|
||||||
system,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
@@ -29,85 +20,36 @@ in
|
|||||||
let
|
let
|
||||||
nixosTestArgs = {
|
nixosTestArgs = {
|
||||||
# reference to nixpkgs for the current system
|
# 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
|
# this gives us a reference to our flake but also all flake inputs
|
||||||
inherit self;
|
inherit self;
|
||||||
inherit (self) clanLib;
|
|
||||||
};
|
};
|
||||||
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||||
|
# import our test
|
||||||
|
secrets = import ./secrets nixosTestArgs;
|
||||||
|
container = import ./container nixosTestArgs;
|
||||||
# Deltachat is currently marked as broken
|
# Deltachat is currently marked as broken
|
||||||
# deltachat = import ./deltachat nixosTestArgs;
|
# deltachat = import ./deltachat nixosTestArgs;
|
||||||
|
borgbackup = import ./borgbackup nixosTestArgs;
|
||||||
# Base Tests
|
matrix-synapse = import ./matrix-synapse nixosTestArgs;
|
||||||
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
|
|
||||||
mumble = import ./mumble nixosTestArgs;
|
mumble = import ./mumble nixosTestArgs;
|
||||||
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
|
|
||||||
data-mesher = import ./data-mesher nixosTestArgs;
|
|
||||||
syncthing = import ./syncthing 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
packagesToBuild = lib.removeAttrs self'.packages [
|
|
||||||
# exclude the check that checks that nothing depends on the repo root
|
|
||||||
# We might want to include this later once everything is fixed
|
|
||||||
"dont-depend-on-repo-root"
|
|
||||||
];
|
|
||||||
|
|
||||||
flakeOutputs =
|
flakeOutputs =
|
||||||
lib.mapAttrs' (
|
lib.mapAttrs' (
|
||||||
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
|
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
|
||||||
) (lib.filterAttrs (n: _: !lib.hasPrefix "test-" n) self.nixosConfigurations)
|
) self.nixosConfigurations
|
||||||
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") packagesToBuild
|
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
|
||||||
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
|
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
|
||||||
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
|
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
|
||||||
self'.legacyPackages.homeConfigurations or { }
|
self'.legacyPackages.homeConfigurations or { }
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
nixosTests
|
nixosTests // flakeOutputs;
|
||||||
// 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
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
legacyPackages = {
|
legacyPackages = {
|
||||||
nixosTests =
|
nixosTests =
|
||||||
let
|
let
|
||||||
@@ -122,8 +64,6 @@ in
|
|||||||
# import our test
|
# import our test
|
||||||
secrets = import ./secrets nixosTestArgs;
|
secrets = import ./secrets nixosTestArgs;
|
||||||
container = import ./container 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 =
|
perSystem =
|
||||||
{
|
{
|
||||||
|
nodes,
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
@@ -43,21 +10,17 @@
|
|||||||
let
|
let
|
||||||
dependencies = [
|
dependencies = [
|
||||||
pkgs.disko
|
pkgs.disko
|
||||||
pkgs.buildPackages.xorg.lndir
|
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine.config.system.build.toplevel
|
||||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.ConfigIniFiles
|
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine.config.system.build.diskoScript
|
||||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.FileSlurp
|
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
|
||||||
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
|
|
||||||
|
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
# Currently disabled...
|
||||||
flash = self.clanLib.test.baseTest {
|
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
||||||
|
flash = (import ../lib/test-base.nix) {
|
||||||
name = "flash";
|
name = "flash";
|
||||||
nodes.target = {
|
nodes.target = {
|
||||||
virtualisation.emptyDiskImages = [ 4096 ];
|
virtualisation.emptyDiskImages = [ 4096 ];
|
||||||
@@ -79,9 +42,7 @@
|
|||||||
testScript = ''
|
testScript = ''
|
||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
# Some distros like to automount disks with spaces
|
machine.succeed("clan flash write --debug --flake ${../..} --yes --disk main /dev/vdb test-install-machine")
|
||||||
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}")
|
|
||||||
'';
|
'';
|
||||||
} { inherit pkgs self; };
|
} { inherit pkgs self; };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
[
|
[
|
||||||
pkgs.gitMinimal
|
pkgs.gitMinimal
|
||||||
pkgs.nix
|
pkgs.nix
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.rsync # needed to have rsync installed on the dummy ssh server
|
pkgs.rsync # needed to have rsync installed on the dummy ssh server
|
||||||
]
|
]
|
||||||
++ self'.packages.clan-cli-full.runtimeDependencies
|
++ self'.packages.clan-cli-full.runtimeDependencies
|
||||||
@@ -31,12 +30,7 @@
|
|||||||
# this disables dynamic dependency loading in clan-cli
|
# this disables dynamic dependency loading in clan-cli
|
||||||
export CLAN_NO_DYNAMIC_DEPS=1
|
export CLAN_NO_DYNAMIC_DEPS=1
|
||||||
|
|
||||||
jobs=$(nproc)
|
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -m impure ./tests $@"
|
||||||
# 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 $@"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,234 +1,145 @@
|
|||||||
{
|
{
|
||||||
self,
|
self,
|
||||||
|
inputs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
{
|
||||||
installer =
|
clan.machines.test-install-machine = {
|
||||||
{ modulesPath, pkgs, ... }:
|
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
|
let
|
||||||
dependencies = [
|
dependencies = [
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
self
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
self.nixosConfigurations.test-install-machine.config.system.build.toplevel
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.clan.deployment.file
|
self.nixosConfigurations.test-install-machine.config.system.build.diskoScript
|
||||||
|
self.nixosConfigurations.test-install-machine.config.system.clan.deployment.file
|
||||||
pkgs.stdenv.drvPath
|
pkgs.stdenv.drvPath
|
||||||
pkgs.bash.drvPath
|
|
||||||
pkgs.nixos-anywhere
|
pkgs.nixos-anywhere
|
||||||
pkgs.bubblewrap
|
|
||||||
pkgs.buildPackages.xorg.lndir
|
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||||
in
|
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:
|
# 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> (finished: waiting for the VM to power off, in 1.97 seconds)
|
||||||
# vm-test-run-test-installation-> server # [test-install-machine] ### Done! ###
|
# vm-test-run-test-installation>
|
||||||
# vm-test-run-test-installation-> server # [test-install-machine] + step 'Done!'
|
# vm-test-run-test-installation> new_machine: must succeed: cat /etc/install-successful
|
||||||
# vm-test-run-test-installation-> server # [test-install-machine] + echo '### Done! ###'
|
# vm-test-run-test-installation> new_machine: waiting for the VM to finish booting
|
||||||
# vm-test-run-test-installation-> server # [test-install-machine] + rm -rf /tmp/tmp.qb16EAq7hJ
|
# vm-test-run-test-installation> new_machine: starting vm
|
||||||
# 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> new_machine: QEMU running (pid 80)
|
||||||
# vm-test-run-test-installation-> target: starting vm
|
# vm-test-run-test-installation> new_machine: Guest root shell did not produce any data yet...
|
||||||
# vm-test-run-test-installation-> target: QEMU running (pid 144)
|
# vm-test-run-test-installation> new_machine: To debug, enter the VM and run 'systemctl status backdoor.service'.
|
||||||
# vm-test-run-test-installation-> target: waiting for unit multi-user.target
|
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux && pkgs.stdenv.hostPlatform.system != "aarch64-linux") {
|
||||||
# vm-test-run-test-installation-> target: waiting for the VM to finish booting
|
test-installation = (import ../lib/test-base.nix) {
|
||||||
# vm-test-run-test-installation-> target: Guest root shell did not produce any data yet...
|
name = "test-installation";
|
||||||
# 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";
|
|
||||||
nodes.target = {
|
nodes.target = {
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
virtualisation.diskImage = "./target.qcow2";
|
virtualisation.diskImage = "./target.qcow2";
|
||||||
virtualisation.useBootLoader = true;
|
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 = ''
|
testScript = ''
|
||||||
|
client.start()
|
||||||
installer.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.state_dir = installer.state_dir
|
||||||
target.start()
|
target.start()
|
||||||
target.wait_for_unit("multi-user.target")
|
target.wait_for_unit("multi-user.target")
|
||||||
'';
|
assert(target.succeed("cat /etc/install-successful").strip() == "ok")
|
||||||
} { 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")
|
|
||||||
'';
|
'';
|
||||||
} { inherit pkgs self; };
|
} { inherit pkgs self; };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,19 +7,9 @@
|
|||||||
let
|
let
|
||||||
testDriver = hostPkgs.python3.pkgs.callPackage ./package.nix {
|
testDriver = hostPkgs.python3.pkgs.callPackage ./package.nix {
|
||||||
inherit (config) extraPythonPackages;
|
inherit (config) extraPythonPackages;
|
||||||
inherit (hostPkgs.pkgs) util-linux systemd nix;
|
inherit (hostPkgs.pkgs) util-linux systemd;
|
||||||
};
|
};
|
||||||
containers =
|
containers = map (m: m.system.build.toplevel) (lib.attrValues config.nodes);
|
||||||
testScript:
|
|
||||||
map (m: [
|
|
||||||
m.system.build.toplevel
|
|
||||||
(hostPkgs.closureInfo {
|
|
||||||
rootPaths = [
|
|
||||||
m.system.build.toplevel
|
|
||||||
(hostPkgs.writeText "testScript" testScript)
|
|
||||||
];
|
|
||||||
})
|
|
||||||
]) (lib.attrValues config.nodes);
|
|
||||||
pythonizeName =
|
pythonizeName =
|
||||||
name:
|
name:
|
||||||
let
|
let
|
||||||
@@ -37,9 +27,6 @@ let
|
|||||||
pythonizedNames = map pythonizeName nodeHostNames;
|
pythonizedNames = map pythonizeName nodeHostNames;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
defaults.imports = [
|
|
||||||
./nixos-module.nix
|
|
||||||
];
|
|
||||||
driver = lib.mkForce (
|
driver = lib.mkForce (
|
||||||
hostPkgs.runCommand "nixos-test-driver-${config.name}"
|
hostPkgs.runCommand "nixos-test-driver-${config.name}"
|
||||||
{
|
{
|
||||||
@@ -57,6 +44,8 @@ in
|
|||||||
''
|
''
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
|
|
||||||
|
containers=(${toString containers})
|
||||||
|
|
||||||
${lib.optionalString (!config.skipTypeCheck) ''
|
${lib.optionalString (!config.skipTypeCheck) ''
|
||||||
# prepend type hints so the test script can be type checked with mypy
|
# prepend type hints so the test script can be type checked with mypy
|
||||||
cat "${./test-script-prepend.py}" >> testScriptWithTypes
|
cat "${./test-script-prepend.py}" >> testScriptWithTypes
|
||||||
@@ -77,13 +66,7 @@ in
|
|||||||
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
||||||
|
|
||||||
wrapProgram $out/bin/nixos-test-driver \
|
wrapProgram $out/bin/nixos-test-driver \
|
||||||
${
|
${lib.concatStringsSep " " (map (name: "--add-flags '--container ${name}'") containers)} \
|
||||||
lib.concatStringsSep " " (
|
|
||||||
map (container: "--add-flags '--container ${builtins.toString container}'") (
|
|
||||||
containers config.testScriptString
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} \
|
|
||||||
--add-flags "--test-script '$out/test-script'"
|
--add-flags "--test-script '$out/test-script'"
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
@@ -92,7 +75,7 @@ in
|
|||||||
lib.lazyDerivation {
|
lib.lazyDerivation {
|
||||||
# lazyDerivation improves performance when only passthru items and/or meta are used.
|
# lazyDerivation improves performance when only passthru items and/or meta are used.
|
||||||
derivation = hostPkgs.stdenv.mkDerivation {
|
derivation = hostPkgs.stdenv.mkDerivation {
|
||||||
name = "container-test-run-${config.name}";
|
name = "vm-test-run-${config.name}";
|
||||||
|
|
||||||
requiredSystemFeatures = [ "uid-range" ];
|
requiredSystemFeatures = [ "uid-range" ];
|
||||||
|
|
||||||
@@ -105,12 +88,6 @@ in
|
|||||||
${config.driver}/bin/nixos-test-driver -o $out
|
${config.driver}/bin/nixos-test-driver -o $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
hostPkgs.util-linux
|
|
||||||
hostPkgs.coreutils
|
|
||||||
hostPkgs.iproute2
|
|
||||||
];
|
|
||||||
|
|
||||||
passthru = config.passthru;
|
passthru = config.passthru;
|
||||||
|
|
||||||
meta = config.meta;
|
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 argparse
|
||||||
import ctypes
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
import uuid
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from contextlib import _GeneratorContextManager
|
from contextlib import _GeneratorContextManager
|
||||||
from dataclasses import dataclass
|
|
||||||
from functools import cached_property
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from colorama import Fore, Style
|
|
||||||
|
|
||||||
from .logger import AbstractLogger, CompositeLogger, TerminalLogger
|
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):
|
class Error(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -116,10 +60,6 @@ class Machine:
|
|||||||
self.rootdir: Path = rootdir
|
self.rootdir: Path = rootdir
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def container_pid(self) -> int:
|
|
||||||
return self.get_systemd_process()
|
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
prepare_machine_root(self.name, self.rootdir)
|
prepare_machine_root(self.name, self.rootdir)
|
||||||
cmd = [
|
cmd = [
|
||||||
@@ -131,16 +71,18 @@ class Machine:
|
|||||||
self.rootdir,
|
self.rootdir,
|
||||||
"--register=no",
|
"--register=no",
|
||||||
"--resolv-conf=off",
|
"--resolv-conf=off",
|
||||||
f"--bind=/.containers/{self.name}/nix:/nix",
|
"--bind-ro=/nix/store",
|
||||||
|
"--bind",
|
||||||
|
self.out_dir,
|
||||||
"--bind=/proc:/run/host/proc",
|
"--bind=/proc:/run/host/proc",
|
||||||
"--bind=/sys:/run/host/sys",
|
"--bind=/sys:/run/host/sys",
|
||||||
"--private-network",
|
"--private-network",
|
||||||
"--network-bridge=br0",
|
|
||||||
self.toplevel.joinpath("init"),
|
self.toplevel.joinpath("init"),
|
||||||
]
|
]
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["SYSTEMD_NSPAWN_UNIFIED_HIERARCHY"] = "1"
|
env["SYSTEMD_NSPAWN_UNIFIED_HIERARCHY"] = "1"
|
||||||
self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True, env=env)
|
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:
|
def get_systemd_process(self) -> int:
|
||||||
assert self.process is not None, "Machine not started"
|
assert self.process is not None, "Machine not started"
|
||||||
@@ -160,9 +102,9 @@ class Machine:
|
|||||||
.read_text()
|
.read_text()
|
||||||
.split()
|
.split()
|
||||||
)
|
)
|
||||||
assert len(childs) == 1, (
|
assert (
|
||||||
f"Expected exactly one child process for systemd-nspawn, got {childs}"
|
len(childs) == 1
|
||||||
)
|
), f"Expected exactly one child process for systemd-nspawn, got {childs}"
|
||||||
try:
|
try:
|
||||||
return int(childs[0])
|
return int(childs[0])
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@@ -191,22 +133,6 @@ class Machine:
|
|||||||
if line_pattern.match(line)
|
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(
|
def execute(
|
||||||
self,
|
self,
|
||||||
command: str,
|
command: str,
|
||||||
@@ -248,10 +174,23 @@ class Machine:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Always run command with shell opts
|
# 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(
|
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,
|
timeout=timeout,
|
||||||
check=False,
|
check=False,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
@@ -314,9 +253,7 @@ class Machine:
|
|||||||
info = self.get_unit_info(unit)
|
info = self.get_unit_info(unit)
|
||||||
state = info["ActiveState"]
|
state = info["ActiveState"]
|
||||||
if state == "failed":
|
if state == "failed":
|
||||||
proc = self.systemctl(f"--lines 0 status {unit}")
|
msg = f'unit "{unit}" reached state "{state}"'
|
||||||
journal = self.execute(f"journalctl -u {unit} --no-pager")
|
|
||||||
msg = f'unit "{unit}" reached state "{state}":\n{proc.stdout}\n{journal.stdout}'
|
|
||||||
raise Error(msg)
|
raise Error(msg)
|
||||||
|
|
||||||
if state == "inactive":
|
if state == "inactive":
|
||||||
@@ -334,18 +271,7 @@ class Machine:
|
|||||||
def succeed(self, command: str, timeout: int | None = None) -> str:
|
def succeed(self, command: str, timeout: int | None = None) -> str:
|
||||||
res = self.execute(command, timeout=timeout)
|
res = self.execute(command, timeout=timeout)
|
||||||
if res.returncode != 0:
|
if res.returncode != 0:
|
||||||
msg = f"Failed to run command {command}\n"
|
msg = f"Failed to run command {command}"
|
||||||
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}"
|
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
return res.stdout
|
return res.stdout
|
||||||
|
|
||||||
@@ -362,70 +288,14 @@ class Machine:
|
|||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
def setup_filesystems() -> None:
|
||||||
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:
|
|
||||||
# We don't care about cleaning up the mount points, since we're running in a nix sandbox.
|
# 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)
|
Path("/run").mkdir(parents=True, exist_ok=True)
|
||||||
subprocess.run(["mount", "-t", "tmpfs", "none", "/run"], check=True)
|
subprocess.run(["mount", "-t", "tmpfs", "none", "/run"], check=True)
|
||||||
subprocess.run(["mount", "-t", "cgroup2", "none", "/sys/fs/cgroup"], 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/os-release").touch()
|
||||||
Path("/etc/machine-id").write_text("a5ea3f98dedc0278b6f3cc8c37eeaeac")
|
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:
|
class Driver:
|
||||||
@@ -433,7 +303,7 @@ class Driver:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
containers: list[ContainerInfo],
|
containers: list[Path],
|
||||||
logger: AbstractLogger,
|
logger: AbstractLogger,
|
||||||
testscript: str,
|
testscript: str,
|
||||||
out_dir: str,
|
out_dir: str,
|
||||||
@@ -442,73 +312,32 @@ class Driver:
|
|||||||
self.testscript = testscript
|
self.testscript = testscript
|
||||||
self.out_dir = out_dir
|
self.out_dir = out_dir
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
|
setup_filesystems()
|
||||||
|
|
||||||
self.tempdir = TemporaryDirectory()
|
self.tempdir = TemporaryDirectory()
|
||||||
tempdir_path = Path(self.tempdir.name)
|
tempdir_path = Path(self.tempdir.name)
|
||||||
|
|
||||||
self.machines = []
|
self.machines = []
|
||||||
for container in containers:
|
for container in containers:
|
||||||
setup_filesystems(container)
|
name_match = re.match(r".*-nixos-system-(.+)-(.+)", container.name)
|
||||||
load_nix_db(container)
|
if not name_match:
|
||||||
|
msg = f"Unable to extract hostname from {container.name}"
|
||||||
|
raise Error(msg)
|
||||||
|
name = name_match.group(1)
|
||||||
self.machines.append(
|
self.machines.append(
|
||||||
Machine(
|
Machine(
|
||||||
name=container.name,
|
name=name,
|
||||||
toplevel=container.toplevel,
|
toplevel=container,
|
||||||
rootdir=tempdir_path / container.name,
|
rootdir=tempdir_path / name,
|
||||||
out_dir=self.out_dir,
|
out_dir=self.out_dir,
|
||||||
logger=self.logger,
|
logger=self.logger,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def start_all(self) -> None:
|
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:
|
for machine in self.machines:
|
||||||
print(f"Starting {machine.name}")
|
|
||||||
machine.start()
|
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]:
|
def test_symbols(self) -> dict[str, Any]:
|
||||||
general_symbols = {
|
general_symbols = {
|
||||||
"start_all": self.start_all,
|
"start_all": self.start_all,
|
||||||
@@ -570,11 +399,9 @@ def main() -> None:
|
|||||||
arg_parser = argparse.ArgumentParser(prog="nixos-test-driver")
|
arg_parser = argparse.ArgumentParser(prog="nixos-test-driver")
|
||||||
arg_parser.add_argument(
|
arg_parser.add_argument(
|
||||||
"--containers",
|
"--containers",
|
||||||
nargs=2,
|
nargs="+",
|
||||||
action="append",
|
|
||||||
type=Path,
|
type=Path,
|
||||||
metavar=("TOPLEVEL_STORE_DIR", "CLOSURE_INFO"),
|
help="container system toplevel paths",
|
||||||
help="container system toplevel store dir and closure info",
|
|
||||||
)
|
)
|
||||||
arg_parser.add_argument(
|
arg_parser.add_argument(
|
||||||
"--test-script",
|
"--test-script",
|
||||||
@@ -591,10 +418,7 @@ def main() -> None:
|
|||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
logger = CompositeLogger([TerminalLogger()])
|
logger = CompositeLogger([TerminalLogger()])
|
||||||
with Driver(
|
with Driver(
|
||||||
containers=[
|
containers=args.containers,
|
||||||
ContainerInfo(toplevel, closure_info)
|
|
||||||
for toplevel, closure_info in args.containers
|
|
||||||
],
|
|
||||||
testscript=args.test_script.read_text(),
|
testscript=args.test_script.read_text(),
|
||||||
out_dir=args.output_directory.resolve(),
|
out_dir=args.output_directory.resolve(),
|
||||||
logger=logger,
|
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
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
(
|
(import ../lib/container-test.nix) (
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
name = "matrix-synapse";
|
name = "matrix-synapse";
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
self.clanModules.matrix-synapse
|
self.clanModules.matrix-synapse
|
||||||
self.nixosModules.clanCore
|
self.nixosModules.clanCore
|
||||||
{
|
{
|
||||||
|
clan.core.settings.machine.name = "machine";
|
||||||
clan.core.settings.directory = ./.;
|
clan.core.settings.directory = ./.;
|
||||||
|
|
||||||
services.nginx.virtualHosts."matrix.clan.test" = {
|
services.nginx.virtualHosts."matrix.clan.test" = {
|
||||||
@@ -30,8 +31,6 @@
|
|||||||
clan.matrix-synapse.users.someuser = { };
|
clan.matrix-synapse.users.someuser = { };
|
||||||
|
|
||||||
clan.core.facts.secretStore = "vm";
|
clan.core.facts.secretStore = "vm";
|
||||||
clan.core.vars.settings.secretStore = "vm";
|
|
||||||
clan.core.vars.settings.publicStore = "in_repo";
|
|
||||||
|
|
||||||
# because we use systemd-tmpfiles to copy the secrets, we need to a separate systemd-tmpfiles call to provision them.
|
# because we use systemd-tmpfiles to copy the secrets, we need to a separate systemd-tmpfiles call to provision them.
|
||||||
boot.postBootCommands = "${config.systemd.package}/bin/systemd-tmpfiles --create /etc/tmpfiles.d/00-vmsecrets.conf";
|
boot.postBootCommands = "${config.systemd.package}/bin/systemd-tmpfiles --create /etc/tmpfiles.d/00-vmsecrets.conf";
|
||||||
@@ -42,21 +41,21 @@
|
|||||||
d.mode = "0700";
|
d.mode = "0700";
|
||||||
z.mode = "0700";
|
z.mode = "0700";
|
||||||
};
|
};
|
||||||
"/etc/secrets/matrix-synapse/synapse-registration_shared_secret" = {
|
"/etc/secrets/synapse-registration_shared_secret" = {
|
||||||
f.argument = "supersecret";
|
f.argument = "supersecret";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"/etc/secrets/matrix-password-admin/matrix-password-admin" = {
|
"/etc/secrets/matrix-password-admin" = {
|
||||||
f.argument = "matrix-password1";
|
f.argument = "matrix-password1";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
user = "root";
|
user = "root";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"/etc/secrets/matrix-password-someuser/matrix-password-someuser" = {
|
"/etc/secrets/matrix-password-someuser" = {
|
||||||
f.argument = "matrix-password2";
|
f.argument = "matrix-password2";
|
||||||
z = {
|
z = {
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
{
|
|
||||||
self,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
clan.machines.test-morph-machine = {
|
|
||||||
imports = [
|
|
||||||
./template/configuration.nix
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
];
|
|
||||||
nixpkgs.hostPlatform = "x86_64-linux";
|
|
||||||
environment.etc."testfile".text = "morphed";
|
|
||||||
};
|
|
||||||
|
|
||||||
clan.templates.machine.test-morph-template = {
|
|
||||||
description = "Morph a machine";
|
|
||||||
path = ./template;
|
|
||||||
};
|
|
||||||
|
|
||||||
perSystem =
|
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) {
|
|
||||||
morph = self.clanLib.test.baseTest {
|
|
||||||
name = "morph";
|
|
||||||
|
|
||||||
nodes = {
|
|
||||||
actual =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
dependencies = [
|
|
||||||
pkgs.stdenv.drvPath
|
|
||||||
pkgs.stdenvNoCC
|
|
||||||
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
|
|
||||||
self.nixosConfigurations.test-morph-machine.config.system.clan.deployment.file
|
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
|
||||||
system.extraDependencies = dependencies;
|
|
||||||
|
|
||||||
virtualisation.memorySize = 2048;
|
|
||||||
virtualisation.useNixStoreImage = true;
|
|
||||||
virtualisation.writableStore = true;
|
|
||||||
|
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli-full ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
testScript = ''
|
|
||||||
start_all()
|
|
||||||
actual.fail("cat /etc/testfile")
|
|
||||||
actual.succeed("env CLAN_DIR=${self.checks.x86_64-linux.clan-core-for-checks} clan machines morph test-morph-template --i-will-be-fired-for-using-this --debug --name test-morph-machine")
|
|
||||||
assert actual.succeed("cat /etc/testfile") == "morphed"
|
|
||||||
'';
|
|
||||||
} { inherit pkgs self; };
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{ modulesPath, ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
# we need these 2 modules always to be able to run the tests
|
|
||||||
(modulesPath + "/testing/test-instrumentation.nix")
|
|
||||||
(modulesPath + "/virtualisation/qemu-vm.nix")
|
|
||||||
|
|
||||||
(modulesPath + "/profiles/minimal.nix")
|
|
||||||
];
|
|
||||||
|
|
||||||
virtualisation.useNixStoreImage = true;
|
|
||||||
virtualisation.writableStore = true;
|
|
||||||
|
|
||||||
clan.core.enableRecommendedDefaults = false;
|
|
||||||
}
|
|
||||||
@@ -1,104 +1,145 @@
|
|||||||
{
|
(import ../lib/test-base.nix) (
|
||||||
pkgs,
|
{ ... }:
|
||||||
self,
|
let
|
||||||
clanLib,
|
common =
|
||||||
...
|
{ self, pkgs, ... }:
|
||||||
}:
|
{
|
||||||
clanLib.test.makeTestClan {
|
imports = [
|
||||||
inherit pkgs self;
|
self.clanModules.mumble
|
||||||
# TODO: container driver does not support: sleep, wait_for_window, send_chars, wait_for_text
|
{
|
||||||
useContainers = false;
|
clan.services.mumble.user = "alice";
|
||||||
nixosTest = (
|
}
|
||||||
{ lib, ... }:
|
self.nixosModules.clanCore
|
||||||
let
|
(self.inputs.nixpkgs + "/nixos/tests/common/x11.nix")
|
||||||
common =
|
{
|
||||||
{ pkgs, modulesPath, ... }:
|
clan.core.settings.directory = ./.;
|
||||||
{
|
environment.systemPackages = [ pkgs.killall ];
|
||||||
imports = [
|
clan.core.facts.services.mumble.secret."mumble-key".path = "/etc/mumble-key";
|
||||||
(modulesPath + "/../tests/common/x11.nix")
|
clan.core.facts.services.mumble.public."mumble-cert".path = "/etc/mumble-cert";
|
||||||
];
|
}
|
||||||
|
];
|
||||||
|
|
||||||
clan.services.mumble.user = "alice";
|
|
||||||
environment.systemPackages = [ pkgs.killall ];
|
|
||||||
};
|
|
||||||
machines = [
|
|
||||||
"peer1"
|
|
||||||
"peer2"
|
|
||||||
];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
name = "mumble";
|
|
||||||
|
|
||||||
clan = {
|
|
||||||
directory = ./.;
|
|
||||||
inventory = {
|
|
||||||
machines = lib.genAttrs machines (_: { });
|
|
||||||
services = {
|
|
||||||
mumble.default = {
|
|
||||||
roles.server.machines = machines;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "mumble";
|
||||||
|
|
||||||
enableOCR = true;
|
enableOCR = true;
|
||||||
|
|
||||||
nodes.peer1 = common;
|
nodes.peer1 =
|
||||||
nodes.peer2 = common;
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
common
|
||||||
|
{
|
||||||
|
clan.core.settings.machine.name = "peer1";
|
||||||
|
environment.etc = {
|
||||||
|
"mumble-key".source = ./peer_1/peer_1_test_key;
|
||||||
|
"mumble-cert".source = ./peer_1/peer_1_test_cert;
|
||||||
|
};
|
||||||
|
systemd.tmpfiles.settings."vmsecrets" = {
|
||||||
|
"/var/lib/murmur/sslKey" = {
|
||||||
|
C.argument = "${./peer_1/peer_1_test_key}";
|
||||||
|
z = {
|
||||||
|
mode = "0400";
|
||||||
|
user = "murmur";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/var/lib/murmur/sslCert" = {
|
||||||
|
C.argument = "${./peer_1/peer_1_test_cert}";
|
||||||
|
z = {
|
||||||
|
mode = "0400";
|
||||||
|
user = "murmur";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
clan.core.facts.services.mumble.secret."mumble-key".path = "/etc/mumble-key";
|
||||||
|
clan.core.facts.services.mumble.public."mumble-cert".path = "/etc/mumble-cert";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
nodes.peer2 =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
common
|
||||||
|
{
|
||||||
|
clan.core.settings.machine.name = "peer2";
|
||||||
|
environment.etc = {
|
||||||
|
"mumble-key".source = ./peer_2/peer_2_test_key;
|
||||||
|
"mumble-cert".source = ./peer_2/peer_2_test_cert;
|
||||||
|
};
|
||||||
|
systemd.tmpfiles.settings."vmsecrets" = {
|
||||||
|
"/var/lib/murmur/sslKey" = {
|
||||||
|
C.argument = "${./peer_2/peer_2_test_key}";
|
||||||
|
z = {
|
||||||
|
mode = "0400";
|
||||||
|
user = "murmur";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/var/lib/murmur/sslCert" = {
|
||||||
|
C.argument = "${./peer_2/peer_2_test_cert}";
|
||||||
|
z = {
|
||||||
|
mode = "0400";
|
||||||
|
user = "murmur";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
testScript = ''
|
with subtest("Waiting for x"):
|
||||||
start_all()
|
peer1.wait_for_x()
|
||||||
|
peer2.wait_for_x()
|
||||||
|
|
||||||
with subtest("Waiting for x"):
|
with subtest("Waiting for murmur"):
|
||||||
peer1.wait_for_x()
|
peer1.wait_for_unit("murmur.service")
|
||||||
peer2.wait_for_x()
|
peer2.wait_for_unit("murmur.service")
|
||||||
|
|
||||||
with subtest("Waiting for murmur"):
|
with subtest("Starting Mumble"):
|
||||||
peer1.wait_for_unit("murmur.service")
|
# starting mumble is blocking
|
||||||
peer2.wait_for_unit("murmur.service")
|
peer1.execute("mumble >&2 &")
|
||||||
|
peer2.execute("mumble >&2 &")
|
||||||
|
|
||||||
with subtest("Starting Mumble"):
|
with subtest("Wait for Mumble"):
|
||||||
# starting mumble is blocking
|
peer1.wait_for_window(r"^Mumble$")
|
||||||
peer1.execute("mumble >&2 &")
|
peer2.wait_for_window(r"^Mumble$")
|
||||||
peer2.execute("mumble >&2 &")
|
|
||||||
|
|
||||||
with subtest("Wait for Mumble"):
|
with subtest("Wait for certificate creation"):
|
||||||
peer1.wait_for_window(r"^Mumble$")
|
peer1.wait_for_window(r"^Mumble$")
|
||||||
peer2.wait_for_window(r"^Mumble$")
|
peer1.sleep(3) # mumble is slow to register handlers
|
||||||
|
peer1.send_chars("\n")
|
||||||
|
peer1.send_chars("\n")
|
||||||
|
peer2.wait_for_window(r"^Mumble$")
|
||||||
|
peer2.sleep(3) # mumble is slow to register handlers
|
||||||
|
peer2.send_chars("\n")
|
||||||
|
peer2.send_chars("\n")
|
||||||
|
|
||||||
with subtest("Wait for certificate creation"):
|
with subtest("Wait for server connect"):
|
||||||
peer1.wait_for_window(r"^Mumble$")
|
peer1.wait_for_window(r"^Mumble Server Connect$")
|
||||||
peer1.sleep(3) # mumble is slow to register handlers
|
peer2.wait_for_window(r"^Mumble Server Connect$")
|
||||||
peer1.send_chars("\n")
|
|
||||||
peer1.send_chars("\n")
|
|
||||||
peer2.wait_for_window(r"^Mumble$")
|
|
||||||
peer2.sleep(3) # mumble is slow to register handlers
|
|
||||||
peer2.send_chars("\n")
|
|
||||||
peer2.send_chars("\n")
|
|
||||||
|
|
||||||
with subtest("Wait for server connect"):
|
with subtest("Check validity of server certificates"):
|
||||||
peer1.wait_for_window(r"^Mumble Server Connect$")
|
peer1.execute("killall .mumble-wrapped")
|
||||||
peer2.wait_for_window(r"^Mumble Server Connect$")
|
peer1.sleep(1)
|
||||||
|
peer1.execute("mumble mumble://peer2 >&2 &")
|
||||||
|
peer1.wait_for_window(r"^Mumble$")
|
||||||
|
peer1.sleep(3) # mumble is slow to register handlers
|
||||||
|
peer1.send_chars("\n")
|
||||||
|
peer1.send_chars("\n")
|
||||||
|
peer1.wait_for_text("Connected.")
|
||||||
|
|
||||||
with subtest("Check validity of server certificates"):
|
peer2.execute("killall .mumble-wrapped")
|
||||||
peer1.execute("killall .mumble-wrapped")
|
peer2.sleep(1)
|
||||||
peer1.sleep(1)
|
peer2.execute("mumble mumble://peer1 >&2 &")
|
||||||
peer1.execute("mumble mumble://peer2 >&2 &")
|
peer2.wait_for_window(r"^Mumble$")
|
||||||
peer1.wait_for_window(r"^Mumble$")
|
peer2.sleep(3) # mumble is slow to register handlers
|
||||||
peer1.sleep(3) # mumble is slow to register handlers
|
peer2.send_chars("\n")
|
||||||
peer1.send_chars("\n")
|
peer2.send_chars("\n")
|
||||||
peer1.send_chars("\n")
|
peer2.wait_for_text("Connected.")
|
||||||
peer1.wait_for_text("Connected.")
|
'';
|
||||||
|
}
|
||||||
peer2.execute("killall .mumble-wrapped")
|
)
|
||||||
peer2.sleep(1)
|
|
||||||
peer2.execute("mumble mumble://peer1 >&2 &")
|
|
||||||
peer2.wait_for_window(r"^Mumble$")
|
|
||||||
peer2.sleep(3) # mumble is slow to register handlers
|
|
||||||
peer2.send_chars("\n")
|
|
||||||
peer2.send_chars("\n")
|
|
||||||
peer2.wait_for_text("Connected.")
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1987metkajgdefk0sfhjqjjtczy9eu2lsg700rwcac6hhy2alhdsshjmpw8",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1fndalxxeduekn5s8q3znl73vjfx2n8kydylyrc2j3aurc93pypvs6pcql4",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:TfEsytctWPCLuo/icbicgRfy7O/txYCllTiLiUlusagGShZyXyIR46TNL9E4XWI2Lce9hIn8zczOdUWaEFPuXcvRMMMWILY3DzI=,iv:zDdq0rdYz/KIwKvIiu9MvKyX9v1pWYxZG3F/7KllBa0=,tag:mTPJGmJ+tKrgYaCZXJ37Nw==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2MmFpbUJuNzRnNGRlQXcy\naEhRanpHbjZpbFZxVkZ2TXFJWk8xYm9lYmlVCmVhRFdDZyt4SjJick1CdnZseWx1\nMGdvaTBYekdBeFUyaHEvTzNJVVM4TncKLS0tIG8rZ1kyTFJTRndQNFVXOC9OTTc5\nZHZGVW1FTzlLQ0RRcjNWeEpVWmVKMDgK7UDm509nexdHqG2xU8CBDZkRStjQIAAN\nDmOz5A8uWpIiyvU2LdOBcc/FQKHaXjB7OAmfT03nJccOeqSF2N3N3g==\n-----END AGE ENCRYPTED FILE-----\n"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2025-04-16T16:40:26Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:5Qe20lbqERvSM5fDY9Orhrtv2U6zholh6uHMq0CqV1OOg+vVWSlqTqJrtz2rD/qQTUECRKzWUHB1D/kgLrJ33lRoEMqrhjmvBfxtDnNjLzoYITlLcYOm9qiv3gOqcrpdBKW10YyNlGP/+Q377Lfbo8tcZ8nmuaT8qA9PYr+AKcs=,iv:IIJEFAvoX9SY3jvkD0xVe1/L6iRPMyzmxeRmpGvZI0I=,tag:1D3BBUjj1suNeL+mVYDiKw==,type:str]",
|
|
||||||
"unencrypted_suffix": "_unencrypted",
|
|
||||||
"version": "3.10.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:NI9y5OdFkBgHf+wfn+ISDL11nh/ud+1RV5SPC64TV4Hvg0w8GKkmjJI5uiGDGI1+FfWwnHWOFexavtM2ZJr/cWfhA6dGKvzrKJc=,iv:itiZFGsGEZD/SH42akh1CLCDbuZxMSj05quMNKwvKg4=,tag:v36FGDDHIuFaABHG9we6ag==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByUVVJek9Ha2ljMkt4U2pi\nSmRRd2g2R0VXZGlySG5TT1E1czFpaWFyNlFjCmRJOThCQWlCNDZnRVRFVHpSTzBW\nOWZCUU5jK2dGQTloOEZMUFFVdk04cXMKLS0tIDVzSTdXRk1UZ3psd29kdnVUcitM\nbFlqb0srUGFCVUhlNzU1dUdTTUkwN0UKAIslz1WCMZWrE+aLPJjeM+wZSXMmwnqx\nyRZT5vVzCPWv2r8sbIjhi1rFbkfF+NXHkzNZD9NS4zddwsDsz5HO1g==\n-----END AGE ENCRYPTED FILE-----\n"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2025-04-16T16:40:48Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:2iDDnVdLPWxYcjdZrDlTb8PzPVOPEZ06QXCFvnZ2gf8ioXPiSY69ZAHRHTGpqCEp5Ve7qTIELbNja2TGU0ONLIcIRWyzqgc4q+G3n2V5fYQURW114pzaK0Ct6r6yR9oZQy8H66uEYQafkyuN2R9++3w5G0LGj8UovPcYQqNEQVo=,iv:TkCAdIgjRpZpsnhhvTfMqGVD/IveFyobYa9SExFWcC4=,tag:4RLhumGqeLT15waqHT0mRg==,type:str]",
|
|
||||||
"unencrypted_suffix": "_unencrypted",
|
|
||||||
"version": "3.10.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDfzCCAmegAwIBAgIUH9AKYdV75FHHBcR4mgfTZB/7eEcwDQYJKoZIhvcNAQEL
|
|
||||||
BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
|
|
||||||
DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAM
|
|
||||||
BgNVBAMMBXBlZXIxMB4XDTI1MDQxNjE2NDAzN1oXDTI1MDUxNjE2NDAzN1owaDEL
|
|
||||||
MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBG
|
|
||||||
cmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAMBgNVBAMM
|
|
||||||
BXBlZXIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80mo3OFSaW8F
|
|
||||||
Ni/W7WZ70bJoGGFPFK17kiRgPu6+ghDiinmzlAQOt8A/u+egl4FsvT9Oz99TjCN1
|
|
||||||
zkK3I74ItKmumpGKGPp92bpm62vQZa4g861xKqLlcbOwJwcfofwa8r4PhhjDhdXS
|
|
||||||
k9vsgiwy0N5FEga79QbDEO/qwSvY+O8yKNG+lNXeOetymKvVbudL8A0je150vmpg
|
|
||||||
oYfYjH57Oa7DpGaIrOpbZsmaBlYHD5dhfJbuX0Gxuq42gkfcBtxv3NbY0NoPVZFV
|
|
||||||
jOvhVPyV9Xme/3JAQUSti+Fd2ZfJ+Ayl90ElA5wk25T1JBEEnMYQlQVBqPawX87C
|
|
||||||
i1EtOysfxQIDAQABoyEwHzAdBgNVHQ4EFgQUFtjyWNCF1Yxd8ymIZ4kE9fXMY5Yw
|
|
||||||
DQYJKoZIhvcNAQELBQADggEBAAHiQcWDvZjN2VTaWY2cQMYy3m8wkdoJTR20uV2z
|
|
||||||
MpjY4KwCiMzTtsFe2LhiYMYFETwqHpG+B6ElOghh/+F8l96vQRbcVI9I3XTKs0G4
|
|
||||||
+zdUtMOyB2XZumB4HBQa3PiXXrA4kAGJV88y5QC4UkZMw6SfwjW8OrtQ5Jim4vUB
|
|
||||||
PZxY75ZIjw4JhknTqKNua7xehY4TBghRrGZAlD4eon7Yc5bIew6Gw5LHIoszOZgk
|
|
||||||
9CFEo1XLN5z8aL9L+V8dh2DNNqF4KiXCRNgwqLmLoepL2Xptd90AOZsBI9mGxMP9
|
|
||||||
YUPsnzcGqcat1x6Fi2Guw++ESDxUp6qKjMGAxPzSXje/TiM=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/machines/peer1
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:IyVffgpj8MSp2pKoic3+UMYBbeu6Fo/BGslSNQpNoURuulqSze7AZPXry/PnXEWAEvoqkfUURAq8kZdWxu5Go6N8YSkd6/sVOScw2Osa3v/qh9ysEPCuoIwYY1/h9IfUmzNM8Q3hgF4BXhb5D1F2cFQ0fZM8dQU1jn8J0DTfwXlR9ALgWRoCCAXHQxBBc1SYRMlnXbbCACfRxkJHGAtQbwwzsGo/as/Ng+YVUiXdQyLJUv3hwGax793pyX8YKQl+Bu1G3+qZJGlL3lkjL5T/7HIIs81YN9XgsT7G9kvMmR4tJWpHCHGHxU9aofSHSTxUX1lil7GUsR6xSefFEIgbZaQ27KUXwKrlZ8dQKJZIwCiUczkMtrdwBZlKkzqlD+Hi5yGSSUsIQ+lvjeR74UwPXOX/HtvGA+mEgcTSO48FCWmU7ymjvTXyoVf9ui75IZjRyHKfpM8WlFHS+lXCSEN+B6HHHMIEeo1nOpKk6e2qRd28oJHwzoqlms63YFEbPd4d2DTGNVAtO3nhXe4+5IqzvR1DZHoNQO7AiDDAJiz5DKPoOkq/u7FEBVewKQWgZyvt09vC0X+1kxqXeeOXmJILLuRngri5BKLS3YR3hgmKbN31G4KIQm3lJUoE6F0c0Opu5lAJZStwMQcwmflrXdlwYsuzMfc48wcWQYtKenuG+r5DzR0je6iwxaru3XWf/aqUnga700+rFMz5dUVOy8IlJYmpd2hdpjwriTcJyV4KwplWzdjhnb7csjrzgAiHJ5iF2MHzfVgVGje0pk6An2+iUEqoh+fmFRVlA9ZZ923Ksxl+iYRn+29Cde/JgdZu5EywDHHs3yHjfbMnFvoXuAOJ/2TX3cA95DjD2+OGvd78jKExF+Q9rda5IdU6kb93QKVf+OyOSNpTdQhKnJRusou5Q255PqoTguiJIMDoH00qGA2y5k/5q49wl9Xq5jNiU70fyaygoPOfxbghfIeIjT0z2/awaXoUW33jAqXylxY0gTTTNPZI7w4d6hRqixgU27dyCkdAQfN+bnCe6ExXSLczmZf8UfGegjRm4lyKOKgX/QLLgRwStB6BBHGn+a+8NrAH9DFgpof4fJ74LN7Vx5oQTIGbQTvSkjGy4VOez0ajmqjQI516XrBvFbLfAnYA1jGaR0ZRkD6ALISdwFhjlycAc2tAffv9F9WKsrVKWvCU3pqGV0OmWtk5chqWrfvrFY7tj0aVZUUHZBBynksiUx/8Tt5unS3OUap86FiPUknvh5lPEPZqrZc/TNiCN+8oLFAtkOXyEnFSJjZSOB/vLcd+lMkWhcGa+usvE1hGZEaiTOXsN9IOihjcZjcR2YYHR0LlkFxzzlN9ILllt7W1cgKApCovZVmtJuU3fCwjOocP1iBeAFNQfzgJ+6gMqXN8oufCrw7g4v8Y0hNjjEJEriufO+qLIg3nHivcgwy4Cp/fL9wof9UFsaKl2okbP66im7BuDcO/x8ZQ+lk9OjWPSHFJTAPScgiwPchwn74aaHiihjhocyWQ2uVzKn9JkBFkMcoUlAz7xwUFBW7AYvVS/dFQA0xF1l4aki7JdoK6L1JptEu1KpbQ2UHLoIp+W0jFB2MRnSDOEtUHMR9af/vHtgV4xoZoCCHDz/PAlK74C2wK20LO1FoQJdch2/IO6bGiAj832wrLKDp4T2VPooS/hqd6U0Jmvtvcv0vdKbewCDgbfMH3lcydwXl4eQh5lDE8Fm+HXo2Zt8lvqRexeqzyIvGy1P/ndx21uxuacTkp3yi2qTHrdy3IkGMIHTZtfQATRgbxMEpUwHyk2hr08RNqlitbHzgor2+Gn6J/DaLuzvfaFl4p5npjNJ5dGjQqjj5VEz/L16VDeBLL7b9BKorqb7LvZUlqVcCYBcd7S7mHUHT4GhlLaEb67PxA6eJbhdVtyB8o7sTE3tGY8OdBg0ClJB2cEJHS1SqQRS+GdIhuWwZS+Go7YCx9EnnqezMMuEK4lfB+LCGaVDj32XNVtaBhDvdxPuBN5QFgjbWMIOGFV8O3l9dTrlDDwYpmFANG0Wz0pjOzX4CL4LXxNSu9k2SSdPw4iKoe/7Rl3yxVsKhrLOQPW3M6Hc/vULNBMvZxRH32zPE4qKo9SD+h6rwBrC9JiwrWdLJQHtu6Yk8GT5agQNCVC1pogvHVvB7+15UdZ7yjyhUONGOfIHDB2peYame3gF0Nce3dbzqUUE5tNuD4FyKkYZbGNr9WDD9nX1i/7EboDnFh2x6pAU+MRl4oyW0dtHiiTnIR6bE=,iv:IZYhje9AgGRe0gQcodG/PQAaRBipBC/7F8qAkG35cxc=,tag:jpXpm1eghy/668gT0bmqMA==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"recipient": "age1987metkajgdefk0sfhjqjjtczy9eu2lsg700rwcac6hhy2alhdsshjmpw8",
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3MDdhSTZMbXlSdDVNVVZU\ndkFyVVI0eDhOUHZRU2FFalVNR3g5dUY5T25FCnl0aXpZRVpaR1hvdm5kSHplOE0x\nckloNFF3OVhNTnAxY2ZpZjNFV3plVXMKLS0tIG4yU0w2c1VGbDVCTUhYbjVrMXhr\nb0dpUnp2YUFWSERSRTVVK3g0WTNKWE0KpUfYS71F/1J1G38/ymd/+bWhABmze1GC\nehgSMymmVdsq+ZjHdJ1XcCyecsn/9aFcaZkEbASiLU8ecLNQOEGgRQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWeXBUOU13M2VvZVNBNUZW\nMy9VV1dMV1FlQU9qekhZWitwb3JISTFwdENBCnB5ZHpNK29DRHBoZ2M4dEJ6UVpq\nWHFOM1lYS0ROQ2NpSTNUdkZqUkorWGsKLS0tIDhaalVJNE1oU0N3WUtodnlsQWla\nUTVmTnhPTHVCWXUyK1ZESGR1Ym5CMXcK3YqyKO/FTdxcxVy5zBGg+JCOWMBOxqd2\n9+FgUJaYaizGy+HLpP5jgtjgz7k504yqEQCo9aQ1CzbvNHom5tAu7A==\n-----END AGE ENCRYPTED FILE-----\n"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2025-04-16T16:40:41Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:R8fWg7Vwq2mnjbTTtyYuLWwrmB6TZYZVx9xPcO5NOvGAABNIxtAVSe9yTpV25OlJiXruTNhPHDxfjwDW8Nad47Sd9fV9QzH36uygT9DOaVrrOD/TH5ojvpCuognofuJ8YHgUsq+yhiQs0QKi5efUrtRVDcXXr8s/UazyuG3vYzk=,iv:eBpSr8GKvG51govZWtqTVMWsWZDctDQ2vVgMm/jq62U=,tag:Yth78awXPAPa/7J+WxTDug==,type:str]",
|
|
||||||
"unencrypted_suffix": "_unencrypted",
|
|
||||||
"version": "3.10.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/users/admin
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDfzCCAmegAwIBAgIUYuUk46fwZ4CBcJ40NWnT9VDIEPUwDQYJKoZIhvcNAQEL
|
|
||||||
BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
|
|
||||||
DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAM
|
|
||||||
BgNVBAMMBXBlZXIyMB4XDTI1MDQxNjE2NDA1OVoXDTI1MDUxNjE2NDA1OVowaDEL
|
|
||||||
MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBG
|
|
||||||
cmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAMBgNVBAMM
|
|
||||||
BXBlZXIyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA45nKnn0r3HwU
|
|
||||||
qqSRuOXbou8zpdf+5i+e1h7pmunXR7WPxPBP09t6i+99BO27GcID59zGMquabpNS
|
|
||||||
dFhj+p+KZkqN+4sokZmyBU1civQqiwX2n5KtoaG0fU3gFFK6pfx3OQawQ6mJ50GU
|
|
||||||
HhA2R3CuA0rXcssr6oPynj9z6pbaL7mKckOWE804xIWZuMEoWNdQEKmUmE5d1ioa
|
|
||||||
edlblzwhqZSS+zAAeUvmb+YUEL6T54lCYYqPPnmwmiwfYFSBGu/SGyFtIijbCuIZ
|
|
||||||
TJMDzzutx1/3Dsv2pOKC0uPb5qRcmdRePAzgBFSna4MNgfbpGHFkGPJgjiue0VIC
|
|
||||||
qyedlpF5UQIDAQABoyEwHzAdBgNVHQ4EFgQUuIeLdxGVyhFbgFRtFbPIIJWw1R0w
|
|
||||||
DQYJKoZIhvcNAQELBQADggEBAFj26XejazrXOfa67o8vGoZrR2TGXOLFWFeplO8B
|
|
||||||
29AruG9poH+sInyxYo1RWAQLQMfDud/yGg73EeYylULbG1bBznKYLLHdvy4l6eXt
|
|
||||||
SEVkEMruH0Kw93zt+NqvSO3bHCX+la1rjizyDcD4iu93xUg2uPSBmVpVpW/aeBCN
|
|
||||||
3eF4FbBocUexmIWaygmMPY5yFY2tAf+OinBf4uSWcKEpFikIqAxQWRSDMWm8xFwY
|
|
||||||
CG7rhfpwDauagpZtkjKkrrRedhdfGiXbxOVtYlBULuUMOggEI+ElpbD0UhyEYCsD
|
|
||||||
XoJn7AOC0sYCGpj2F1ESwFX/5EhyciLjMuVwohFVcyWWg+Q=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user