Merge pull request 'change from nixpkgs-fmt to rfc style formatter' (#995) from openssh into main
Reviewed-on: #995
This commit is contained in:
commit
3555001c0d
@ -14,21 +14,27 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.nixosConfigurations = { inherit (clan.nixosConfigurations) test_backup_client; };
|
flake.nixosConfigurations = {
|
||||||
|
inherit (clan.nixosConfigurations) test_backup_client;
|
||||||
|
};
|
||||||
flake.clanInternals = clan.clanInternals;
|
flake.clanInternals = clan.clanInternals;
|
||||||
flake.nixosModules = {
|
flake.nixosModules = {
|
||||||
test_backup_server = { ... }: {
|
test_backup_server =
|
||||||
imports = [
|
{ ... }:
|
||||||
self.clanModules.borgbackup
|
{
|
||||||
];
|
imports = [ self.clanModules.borgbackup ];
|
||||||
services.sshd.enable = true;
|
services.sshd.enable = true;
|
||||||
services.borgbackup.repos.testrepo = {
|
services.borgbackup.repos.testrepo = {
|
||||||
authorizedKeys = [
|
authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
|
||||||
(builtins.readFile ../lib/ssh/pubkey)
|
};
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
test_backup_client =
|
||||||
test_backup_client = { pkgs, lib, config, ... }:
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
dependencies = [
|
dependencies = [
|
||||||
self
|
self
|
||||||
@ -38,14 +44,10 @@ in
|
|||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [ self.clanModules.borgbackup ];
|
||||||
self.clanModules.borgbackup
|
|
||||||
];
|
|
||||||
networking.hostName = "client";
|
networking.hostName = "client";
|
||||||
services.sshd.enable = true;
|
services.sshd.enable = true;
|
||||||
users.users.root.openssh.authorizedKeys.keyFiles = [
|
users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
|
||||||
../lib/ssh/pubkey
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.tmpfiles.settings."vmsecrets" = {
|
systemd.tmpfiles.settings."vmsecrets" = {
|
||||||
"/etc/secrets/borgbackup.ssh" = {
|
"/etc/secrets/borgbackup.ssh" = {
|
||||||
@ -78,65 +80,64 @@ in
|
|||||||
clan.borgbackup.destinations.test_backup_server.repo = "borg@server:.";
|
clan.borgbackup.destinations.test_backup_server.repo = "borg@server:.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
perSystem = { nodes, pkgs, ... }: {
|
perSystem =
|
||||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
{ nodes, pkgs, ... }:
|
||||||
test-backups =
|
{
|
||||||
(import ../lib/test-base.nix)
|
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
||||||
{
|
test-backups = (import ../lib/test-base.nix) {
|
||||||
name = "test-backups";
|
name = "test-backups";
|
||||||
nodes.server = {
|
nodes.server = {
|
||||||
imports = [
|
imports = [
|
||||||
self.nixosModules.test_backup_server
|
self.nixosModules.test_backup_server
|
||||||
self.nixosModules.clanCore
|
self.nixosModules.clanCore
|
||||||
{
|
{
|
||||||
clanCore.machineName = "server";
|
clanCore.machineName = "server";
|
||||||
clanCore.clanDir = ../..;
|
clanCore.clanDir = ../..;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
nodes.client = {
|
nodes.client = {
|
||||||
imports = [
|
imports = [
|
||||||
self.nixosModules.test_backup_client
|
self.nixosModules.test_backup_client
|
||||||
self.nixosModules.clanCore
|
self.nixosModules.clanCore
|
||||||
{
|
{
|
||||||
clanCore.machineName = "client";
|
clanCore.machineName = "client";
|
||||||
clanCore.clanDir = ../..;
|
clanCore.clanDir = ../..;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
import json
|
import json
|
||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
# setup
|
# setup
|
||||||
client.succeed("mkdir -m 700 /root/.ssh")
|
client.succeed("mkdir -m 700 /root/.ssh")
|
||||||
client.succeed(
|
client.succeed(
|
||||||
"cat ${../lib/ssh/privkey} > /root/.ssh/id_ed25519"
|
"cat ${../lib/ssh/privkey} > /root/.ssh/id_ed25519"
|
||||||
)
|
)
|
||||||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||||
client.wait_for_unit("sshd", timeout=30)
|
client.wait_for_unit("sshd", timeout=30)
|
||||||
client.succeed("ssh -o StrictHostKeyChecking=accept-new root@client hostname")
|
client.succeed("ssh -o StrictHostKeyChecking=accept-new root@client hostname")
|
||||||
|
|
||||||
# dummy data
|
# dummy data
|
||||||
client.succeed("mkdir /var/test-backups")
|
client.succeed("mkdir /var/test-backups")
|
||||||
client.succeed("echo testing > /var/test-backups/somefile")
|
client.succeed("echo testing > /var/test-backups/somefile")
|
||||||
|
|
||||||
# create
|
# create
|
||||||
client.succeed("clan --debug --flake ${../..} backups create test_backup_client")
|
client.succeed("clan --debug --flake ${../..} backups create test_backup_client")
|
||||||
client.wait_until_succeeds("! systemctl is-active borgbackup-job-test_backup_server")
|
client.wait_until_succeeds("! systemctl is-active borgbackup-job-test_backup_server")
|
||||||
|
|
||||||
# list
|
# list
|
||||||
backup_id = json.loads(client.succeed("borg-job-test_backup_server list --json"))["archives"][0]["archive"]
|
backup_id = json.loads(client.succeed("borg-job-test_backup_server list --json"))["archives"][0]["archive"]
|
||||||
assert(backup_id in client.succeed("clan --debug --flake ${../..} backups list test_backup_client"))
|
assert(backup_id in client.succeed("clan --debug --flake ${../..} backups list test_backup_client"))
|
||||||
|
|
||||||
# restore
|
# restore
|
||||||
client.succeed("rm -f /var/test-backups/somefile")
|
client.succeed("rm -f /var/test-backups/somefile")
|
||||||
client.succeed(f"clan --debug --flake ${../..} backups restore test_backup_client borgbackup {backup_id}")
|
client.succeed(f"clan --debug --flake ${../..} backups restore test_backup_client borgbackup {backup_id}")
|
||||||
assert(client.succeed("cat /var/test-backups/somefile").strip() == "testing")
|
assert(client.succeed("cat /var/test-backups/somefile").strip() == "testing")
|
||||||
'';
|
'';
|
||||||
}
|
} { inherit pkgs self; };
|
||||||
{ inherit pkgs self; };
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,51 @@
|
|||||||
(import ../lib/test-base.nix) ({ ... }: {
|
(import ../lib/test-base.nix) (
|
||||||
name = "borgbackup";
|
{ ... }:
|
||||||
|
{
|
||||||
|
name = "borgbackup";
|
||||||
|
|
||||||
nodes.machine = { self, pkgs, ... }: {
|
nodes.machine =
|
||||||
imports = [
|
{ self, pkgs, ... }:
|
||||||
self.clanModules.borgbackup
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
{
|
{
|
||||||
services.openssh.enable = true;
|
imports = [
|
||||||
services.borgbackup.repos.testrepo = {
|
self.clanModules.borgbackup
|
||||||
authorizedKeys = [
|
self.nixosModules.clanCore
|
||||||
(builtins.readFile ../lib/ssh/pubkey)
|
{
|
||||||
];
|
services.openssh.enable = true;
|
||||||
};
|
services.borgbackup.repos.testrepo = {
|
||||||
}
|
authorizedKeys = [ (builtins.readFile ../lib/ssh/pubkey) ];
|
||||||
{
|
|
||||||
clanCore.machineName = "machine";
|
|
||||||
clanCore.clanDir = ./.;
|
|
||||||
clanCore.state.testState.folders = [ "/etc/state" ];
|
|
||||||
environment.etc.state.text = "hello world";
|
|
||||||
systemd.tmpfiles.settings."vmsecrets" = {
|
|
||||||
"/etc/secrets/borgbackup.ssh" = {
|
|
||||||
C.argument = "${../lib/ssh/privkey}";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
"/etc/secrets/borgbackup.repokey" = {
|
{
|
||||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
clanCore.machineName = "machine";
|
||||||
z = {
|
clanCore.clanDir = ./.;
|
||||||
mode = "0400";
|
clanCore.state.testState.folders = [ "/etc/state" ];
|
||||||
user = "root";
|
environment.etc.state.text = "hello world";
|
||||||
|
systemd.tmpfiles.settings."vmsecrets" = {
|
||||||
|
"/etc/secrets/borgbackup.ssh" = {
|
||||||
|
C.argument = "${../lib/ssh/privkey}";
|
||||||
|
z = {
|
||||||
|
mode = "0400";
|
||||||
|
user = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/etc/secrets/borgbackup.repokey" = {
|
||||||
|
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
||||||
|
z = {
|
||||||
|
mode = "0400";
|
||||||
|
user = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
clanCore.secretStore = "vm";
|
||||||
};
|
|
||||||
clanCore.secretStore = "vm";
|
|
||||||
|
|
||||||
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
testScript = ''
|
testScript = ''
|
||||||
start_all()
|
start_all()
|
||||||
machine.systemctl("start --wait borgbackup-job-test.service")
|
machine.systemctl("start --wait borgbackup-job-test.service")
|
||||||
assert "machine-test" in machine.succeed("BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes /run/current-system/sw/bin/borg-job-test list")
|
assert "machine-test" in machine.succeed("BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes /run/current-system/sw/bin/borg-job-test list")
|
||||||
'';
|
'';
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
(import ../lib/container-test.nix) ({ ... }: {
|
(import ../lib/container-test.nix) (
|
||||||
name = "secrets";
|
{ ... }:
|
||||||
|
{
|
||||||
|
name = "secrets";
|
||||||
|
|
||||||
nodes.machine = { ... }: {
|
nodes.machine =
|
||||||
networking.hostName = "machine";
|
{ ... }:
|
||||||
services.openssh.enable = true;
|
{
|
||||||
services.openssh.startWhenNeeded = false;
|
networking.hostName = "machine";
|
||||||
};
|
services.openssh.enable = true;
|
||||||
testScript = ''
|
services.openssh.startWhenNeeded = false;
|
||||||
start_all()
|
};
|
||||||
machine.succeed("systemctl status sshd")
|
testScript = ''
|
||||||
machine.wait_for_unit("sshd")
|
start_all()
|
||||||
'';
|
machine.succeed("systemctl status sshd")
|
||||||
})
|
machine.wait_for_unit("sshd")
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -1,24 +1,29 @@
|
|||||||
(import ../lib/container-test.nix) ({ pkgs, ... }: {
|
(import ../lib/container-test.nix) (
|
||||||
name = "secrets";
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
name = "secrets";
|
||||||
|
|
||||||
nodes.machine = { self, ... }: {
|
nodes.machine =
|
||||||
imports = [
|
{ self, ... }:
|
||||||
self.clanModules.deltachat
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
{
|
{
|
||||||
clanCore.machineName = "machine";
|
imports = [
|
||||||
clanCore.clanDir = ./.;
|
self.clanModules.deltachat
|
||||||
}
|
self.nixosModules.clanCore
|
||||||
];
|
{
|
||||||
};
|
clanCore.machineName = "machine";
|
||||||
testScript = ''
|
clanCore.clanDir = ./.;
|
||||||
start_all()
|
}
|
||||||
machine.wait_for_unit("maddy")
|
];
|
||||||
# imap
|
};
|
||||||
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 143")
|
testScript = ''
|
||||||
# smtp submission
|
start_all()
|
||||||
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 587")
|
machine.wait_for_unit("maddy")
|
||||||
# smtp
|
# imap
|
||||||
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 25")
|
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 143")
|
||||||
'';
|
# smtp submission
|
||||||
})
|
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 587")
|
||||||
|
# smtp
|
||||||
|
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 25")
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -1,41 +1,20 @@
|
|||||||
{ self, ... }: {
|
{ self, ... }:
|
||||||
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./impure/flake-module.nix
|
./impure/flake-module.nix
|
||||||
./backups/flake-module.nix
|
./backups/flake-module.nix
|
||||||
./installation/flake-module.nix
|
./installation/flake-module.nix
|
||||||
./flash/flake-module.nix
|
./flash/flake-module.nix
|
||||||
];
|
];
|
||||||
perSystem = { pkgs, lib, self', ... }: {
|
perSystem =
|
||||||
checks =
|
{
|
||||||
let
|
pkgs,
|
||||||
nixosTestArgs = {
|
lib,
|
||||||
# reference to nixpkgs for the current system
|
self',
|
||||||
inherit pkgs;
|
...
|
||||||
# this gives us a reference to our flake but also all flake inputs
|
}:
|
||||||
inherit self;
|
{
|
||||||
};
|
checks =
|
||||||
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
|
||||||
# import our test
|
|
||||||
secrets = import ./secrets nixosTestArgs;
|
|
||||||
container = import ./container nixosTestArgs;
|
|
||||||
deltachat = import ./deltachat nixosTestArgs;
|
|
||||||
zt-tcp-relay = import ./zt-tcp-relay nixosTestArgs;
|
|
||||||
borgbackup = import ./borgbackup nixosTestArgs;
|
|
||||||
syncthing = import ./syncthing nixosTestArgs;
|
|
||||||
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
|
|
||||||
};
|
|
||||||
schemaTests = pkgs.callPackages ./schemas.nix {
|
|
||||||
inherit self;
|
|
||||||
};
|
|
||||||
|
|
||||||
flakeOutputs = lib.mapAttrs' (name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel) self.nixosConfigurations
|
|
||||||
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
|
|
||||||
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
|
|
||||||
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (self'.legacyPackages.homeConfigurations or { });
|
|
||||||
in
|
|
||||||
nixosTests // schemaTests // flakeOutputs;
|
|
||||||
legacyPackages = {
|
|
||||||
nixosTests =
|
|
||||||
let
|
let
|
||||||
nixosTestArgs = {
|
nixosTestArgs = {
|
||||||
# reference to nixpkgs for the current system
|
# reference to nixpkgs for the current system
|
||||||
@ -43,12 +22,44 @@
|
|||||||
# 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;
|
||||||
};
|
};
|
||||||
|
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||||
|
# import our test
|
||||||
|
secrets = import ./secrets nixosTestArgs;
|
||||||
|
container = import ./container nixosTestArgs;
|
||||||
|
deltachat = import ./deltachat nixosTestArgs;
|
||||||
|
zt-tcp-relay = import ./zt-tcp-relay nixosTestArgs;
|
||||||
|
borgbackup = import ./borgbackup nixosTestArgs;
|
||||||
|
syncthing = import ./syncthing nixosTestArgs;
|
||||||
|
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
|
||||||
|
};
|
||||||
|
schemaTests = pkgs.callPackages ./schemas.nix { inherit self; };
|
||||||
|
|
||||||
|
flakeOutputs =
|
||||||
|
lib.mapAttrs' (
|
||||||
|
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
|
||||||
|
) self.nixosConfigurations
|
||||||
|
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
|
||||||
|
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
|
||||||
|
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
|
||||||
|
self'.legacyPackages.homeConfigurations or { }
|
||||||
|
);
|
||||||
in
|
in
|
||||||
lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
nixosTests // schemaTests // flakeOutputs;
|
||||||
# import our test
|
legacyPackages = {
|
||||||
secrets = import ./secrets nixosTestArgs;
|
nixosTests =
|
||||||
container = import ./container nixosTestArgs;
|
let
|
||||||
};
|
nixosTestArgs = {
|
||||||
|
# reference to nixpkgs for the current system
|
||||||
|
inherit pkgs;
|
||||||
|
# this gives us a reference to our flake but also all flake inputs
|
||||||
|
inherit self;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||||
|
# import our test
|
||||||
|
secrets = import ./secrets nixosTestArgs;
|
||||||
|
container = import ./container nixosTestArgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
{ self, ... }:
|
{ self, ... }:
|
||||||
{
|
{
|
||||||
perSystem = { nodes, pkgs, lib, ... }:
|
perSystem =
|
||||||
|
{
|
||||||
|
nodes,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
dependencies = [
|
dependencies = [
|
||||||
self
|
self
|
||||||
@ -14,33 +20,30 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
||||||
flash =
|
flash = (import ../lib/test-base.nix) {
|
||||||
(import ../lib/test-base.nix)
|
name = "flash";
|
||||||
{
|
nodes.target = {
|
||||||
name = "flash";
|
virtualisation.emptyDiskImages = [ 4096 ];
|
||||||
nodes.target = {
|
virtualisation.memorySize = 3000;
|
||||||
virtualisation.emptyDiskImages = [ 4096 ];
|
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||||
virtualisation.memorySize = 3000;
|
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
|
||||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
|
||||||
|
|
||||||
nix.settings = {
|
nix.settings = {
|
||||||
substituters = lib.mkForce [ ];
|
substituters = lib.mkForce [ ];
|
||||||
hashed-mirrors = null;
|
hashed-mirrors = null;
|
||||||
connect-timeout = lib.mkForce 3;
|
connect-timeout = lib.mkForce 3;
|
||||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||||
experimental-features = [
|
experimental-features = [
|
||||||
"nix-command"
|
"nix-command"
|
||||||
"flakes"
|
"flakes"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
testScript = ''
|
testScript = ''
|
||||||
start_all()
|
start_all()
|
||||||
machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine")
|
machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine")
|
||||||
'';
|
'';
|
||||||
}
|
} { inherit pkgs self; };
|
||||||
{ inherit pkgs self; };
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
{
|
{
|
||||||
perSystem = { pkgs, lib, ... }: {
|
perSystem =
|
||||||
# a script that executes all other checks
|
{ pkgs, lib, ... }:
|
||||||
packages.impure-checks = pkgs.writeShellScriptBin "impure-checks" ''
|
{
|
||||||
#!${pkgs.bash}/bin/bash
|
# a script that executes all other checks
|
||||||
set -euo pipefail
|
packages.impure-checks = pkgs.writeShellScriptBin "impure-checks" ''
|
||||||
|
#!${pkgs.bash}/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
export PATH="${lib.makeBinPath [
|
export PATH="${
|
||||||
pkgs.gitMinimal
|
lib.makeBinPath [
|
||||||
pkgs.nix
|
pkgs.gitMinimal
|
||||||
pkgs.rsync # needed to have rsync installed on the dummy ssh server
|
pkgs.nix
|
||||||
]}"
|
pkgs.rsync # needed to have rsync installed on the dummy ssh server
|
||||||
ROOT=$(git rev-parse --show-toplevel)
|
]
|
||||||
cd "$ROOT/pkgs/clan-cli"
|
}"
|
||||||
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -s -m impure ./tests $@"
|
ROOT=$(git rev-parse --show-toplevel)
|
||||||
'';
|
cd "$ROOT/pkgs/clan-cli"
|
||||||
};
|
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -s -m impure ./tests $@"
|
||||||
|
'';
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -12,26 +12,34 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.nixosConfigurations = { inherit (clan.nixosConfigurations) test_install_machine; };
|
flake.nixosConfigurations = {
|
||||||
|
inherit (clan.nixosConfigurations) test_install_machine;
|
||||||
|
};
|
||||||
flake.clanInternals = clan.clanInternals;
|
flake.clanInternals = clan.clanInternals;
|
||||||
flake.nixosModules = {
|
flake.nixosModules = {
|
||||||
test_install_machine = { lib, modulesPath, ... }: {
|
test_install_machine =
|
||||||
imports = [
|
{ lib, modulesPath, ... }:
|
||||||
self.clanModules.diskLayouts
|
{
|
||||||
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
|
imports = [
|
||||||
(modulesPath + "/profiles/qemu-guest.nix")
|
self.clanModules.diskLayouts
|
||||||
];
|
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
|
||||||
clan.diskLayouts.singleDiskExt4.device = "/dev/vdb";
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
|
];
|
||||||
|
clan.diskLayouts.singleDiskExt4.device = "/dev/vdb";
|
||||||
|
|
||||||
environment.etc."install-successful".text = "ok";
|
environment.etc."install-successful".text = "ok";
|
||||||
|
|
||||||
boot.consoleLogLevel = lib.mkForce 100;
|
boot.consoleLogLevel = lib.mkForce 100;
|
||||||
boot.kernelParams = [
|
boot.kernelParams = [ "boot.shell_on_fail" ];
|
||||||
"boot.shell_on_fail"
|
};
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
perSystem = { nodes, pkgs, lib, ... }:
|
perSystem =
|
||||||
|
{
|
||||||
|
nodes,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
dependencies = [
|
dependencies = [
|
||||||
self
|
self
|
||||||
@ -45,74 +53,69 @@ in
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
||||||
test-installation =
|
test-installation = (import ../lib/test-base.nix) {
|
||||||
(import ../lib/test-base.nix)
|
name = "test-installation";
|
||||||
{
|
nodes.target = {
|
||||||
name = "test-installation";
|
services.openssh.enable = true;
|
||||||
nodes.target = {
|
users.users.root.openssh.authorizedKeys.keyFiles = [ ../lib/ssh/pubkey ];
|
||||||
services.openssh.enable = true;
|
system.nixos.variant_id = "installer";
|
||||||
users.users.root.openssh.authorizedKeys.keyFiles = [
|
virtualisation.emptyDiskImages = [ 4096 ];
|
||||||
../lib/ssh/pubkey
|
nix.settings = {
|
||||||
];
|
substituters = lib.mkForce [ ];
|
||||||
system.nixos.variant_id = "installer";
|
hashed-mirrors = null;
|
||||||
virtualisation.emptyDiskImages = [ 4096 ];
|
connect-timeout = lib.mkForce 3;
|
||||||
nix.settings = {
|
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||||
substituters = lib.mkForce [ ];
|
experimental-features = [
|
||||||
hashed-mirrors = null;
|
"nix-command"
|
||||||
connect-timeout = lib.mkForce 3;
|
"flakes"
|
||||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
];
|
||||||
experimental-features = [
|
};
|
||||||
"nix-command"
|
};
|
||||||
"flakes"
|
nodes.client = {
|
||||||
];
|
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||||
};
|
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||||
};
|
virtualisation.memorySize = 2048;
|
||||||
nodes.client = {
|
nix.settings = {
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
substituters = lib.mkForce [ ];
|
||||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
hashed-mirrors = null;
|
||||||
virtualisation.memorySize = 2048;
|
connect-timeout = lib.mkForce 3;
|
||||||
nix.settings = {
|
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||||
substituters = lib.mkForce [ ];
|
experimental-features = [
|
||||||
hashed-mirrors = null;
|
"nix-command"
|
||||||
connect-timeout = lib.mkForce 3;
|
"flakes"
|
||||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
];
|
||||||
experimental-features = [
|
};
|
||||||
"nix-command"
|
system.extraDependencies = dependencies;
|
||||||
"flakes"
|
};
|
||||||
];
|
|
||||||
};
|
|
||||||
system.extraDependencies = dependencies;
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
def create_test_machine(oldmachine=None, args={}): # taken from <nixpkgs/nixos/tests/installer.nix>
|
def create_test_machine(oldmachine=None, args={}): # taken from <nixpkgs/nixos/tests/installer.nix>
|
||||||
startCommand = "${pkgs.qemu_test}/bin/qemu-kvm"
|
startCommand = "${pkgs.qemu_test}/bin/qemu-kvm"
|
||||||
startCommand += " -cpu max -m 1024 -virtfs local,path=/nix/store,security_model=none,mount_tag=nix-store"
|
startCommand += " -cpu max -m 1024 -virtfs local,path=/nix/store,security_model=none,mount_tag=nix-store"
|
||||||
startCommand += f' -drive file={oldmachine.state_dir}/empty0.qcow2,id=drive1,if=none,index=1,werror=report'
|
startCommand += f' -drive file={oldmachine.state_dir}/empty0.qcow2,id=drive1,if=none,index=1,werror=report'
|
||||||
startCommand += ' -device virtio-blk-pci,drive=drive1'
|
startCommand += ' -device virtio-blk-pci,drive=drive1'
|
||||||
machine = create_machine({
|
machine = create_machine({
|
||||||
"startCommand": startCommand,
|
"startCommand": startCommand,
|
||||||
} | args)
|
} | args)
|
||||||
driver.machines.append(machine)
|
driver.machines.append(machine)
|
||||||
return machine
|
return machine
|
||||||
|
|
||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
client.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../lib/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("ssh -o StrictHostKeyChecking=accept-new -v root@target hostname")
|
client.wait_until_succeeds("ssh -o StrictHostKeyChecking=accept-new -v root@target hostname")
|
||||||
|
|
||||||
client.succeed("clan --debug --flake ${../..} machines install --yes test_install_machine root@target >&2")
|
client.succeed("clan --debug --flake ${../..} machines install --yes test_install_machine root@target >&2")
|
||||||
try:
|
try:
|
||||||
target.shutdown()
|
target.shutdown()
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
# qemu has already exited
|
# qemu has already exited
|
||||||
pass
|
pass
|
||||||
|
|
||||||
new_machine = create_test_machine(oldmachine=target, args={ "name": "new_machine" })
|
new_machine = create_test_machine(oldmachine=target, args={ "name": "new_machine" })
|
||||||
assert(new_machine.succeed("cat /etc/install-successful").strip() == "ok")
|
assert(new_machine.succeed("cat /etc/install-successful").strip() == "ok")
|
||||||
'';
|
'';
|
||||||
}
|
} { inherit pkgs self; };
|
||||||
{ inherit pkgs self; };
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
{ hostPkgs, lib, config, ... }:
|
{
|
||||||
|
hostPkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
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;
|
inherit (hostPkgs.pkgs) util-linux systemd;
|
||||||
};
|
};
|
||||||
containers = map (m: m.system.build.toplevel) (lib.attrValues config.nodes);
|
containers = map (m: m.system.build.toplevel) (lib.attrValues config.nodes);
|
||||||
pythonizeName = name:
|
pythonizeName =
|
||||||
|
name:
|
||||||
let
|
let
|
||||||
head = lib.substring 0 1 name;
|
head = lib.substring 0 1 name;
|
||||||
tail = lib.substring 1 (-1) name;
|
tail = lib.substring 1 (-1) name;
|
||||||
in
|
in
|
||||||
(if builtins.match "[A-z_]" head == null then "_" else head) +
|
(if builtins.match "[A-z_]" head == null then "_" else head)
|
||||||
lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
|
+ lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
|
||||||
nodeHostNames =
|
nodeHostNames =
|
||||||
let
|
let
|
||||||
nodesList = map (c: c.system.name) (lib.attrValues config.nodes);
|
nodesList = map (c: c.system.name) (lib.attrValues config.nodes);
|
||||||
@ -21,68 +27,72 @@ let
|
|||||||
pythonizedNames = map pythonizeName nodeHostNames;
|
pythonizedNames = map pythonizeName nodeHostNames;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
driver = lib.mkForce (hostPkgs.runCommand "nixos-test-driver-${config.name}"
|
driver = lib.mkForce (
|
||||||
{
|
hostPkgs.runCommand "nixos-test-driver-${config.name}"
|
||||||
nativeBuildInputs = [
|
{
|
||||||
hostPkgs.makeWrapper
|
nativeBuildInputs = [
|
||||||
] ++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ];
|
hostPkgs.makeWrapper
|
||||||
buildInputs = [ testDriver ];
|
] ++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ];
|
||||||
testScript = config.testScriptString;
|
buildInputs = [ testDriver ];
|
||||||
preferLocalBuild = true;
|
testScript = config.testScriptString;
|
||||||
passthru = config.passthru;
|
preferLocalBuild = true;
|
||||||
meta = config.meta // {
|
passthru = config.passthru;
|
||||||
mainProgram = "nixos-test-driver";
|
meta = config.meta // {
|
||||||
|
mainProgram = "nixos-test-driver";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
|
||||||
|
containers=(${toString containers})
|
||||||
|
|
||||||
|
${lib.optionalString (!config.skipTypeCheck) ''
|
||||||
|
# prepend type hints so the test script can be type checked with mypy
|
||||||
|
cat "${./test-script-prepend.py}" >> testScriptWithTypes
|
||||||
|
echo "${builtins.toString machineNames}" >> testScriptWithTypes
|
||||||
|
echo -n "$testScript" >> testScriptWithTypes
|
||||||
|
|
||||||
|
echo "Running type check (enable/disable: config.skipTypeCheck)"
|
||||||
|
echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipTypeCheck"
|
||||||
|
|
||||||
|
mypy --no-implicit-optional \
|
||||||
|
--pretty \
|
||||||
|
--no-color-output \
|
||||||
|
testScriptWithTypes
|
||||||
|
''}
|
||||||
|
|
||||||
|
echo -n "$testScript" >> $out/test-script
|
||||||
|
|
||||||
|
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
||||||
|
|
||||||
|
wrapProgram $out/bin/nixos-test-driver \
|
||||||
|
${lib.concatStringsSep " " (map (name: "--add-flags '--container ${name}'") containers)} \
|
||||||
|
--add-flags "--test-script '$out/test-script'"
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
test = lib.mkForce (
|
||||||
|
lib.lazyDerivation {
|
||||||
|
# lazyDerivation improves performance when only passthru items and/or meta are used.
|
||||||
|
derivation = hostPkgs.stdenv.mkDerivation {
|
||||||
|
name = "vm-test-run-${config.name}";
|
||||||
|
|
||||||
|
requiredSystemFeatures = [ "uid-range" ];
|
||||||
|
|
||||||
|
buildCommand = ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
|
# effectively mute the XMLLogger
|
||||||
|
export LOGFILE=/dev/null
|
||||||
|
|
||||||
|
${config.driver}/bin/nixos-test-driver -o $out
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru = config.passthru;
|
||||||
|
|
||||||
|
meta = config.meta;
|
||||||
};
|
};
|
||||||
|
inherit (config) passthru meta;
|
||||||
}
|
}
|
||||||
''
|
);
|
||||||
mkdir -p $out/bin
|
|
||||||
|
|
||||||
containers=(${toString containers})
|
|
||||||
|
|
||||||
${lib.optionalString (!config.skipTypeCheck) ''
|
|
||||||
# prepend type hints so the test script can be type checked with mypy
|
|
||||||
cat "${./test-script-prepend.py}" >> testScriptWithTypes
|
|
||||||
echo "${builtins.toString machineNames}" >> testScriptWithTypes
|
|
||||||
echo -n "$testScript" >> testScriptWithTypes
|
|
||||||
|
|
||||||
echo "Running type check (enable/disable: config.skipTypeCheck)"
|
|
||||||
echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipTypeCheck"
|
|
||||||
|
|
||||||
mypy --no-implicit-optional \
|
|
||||||
--pretty \
|
|
||||||
--no-color-output \
|
|
||||||
testScriptWithTypes
|
|
||||||
''}
|
|
||||||
|
|
||||||
echo -n "$testScript" >> $out/test-script
|
|
||||||
|
|
||||||
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
|
|
||||||
|
|
||||||
wrapProgram $out/bin/nixos-test-driver \
|
|
||||||
${lib.concatStringsSep " " (map (name: "--add-flags '--container ${name}'") containers)} \
|
|
||||||
--add-flags "--test-script '$out/test-script'"
|
|
||||||
'');
|
|
||||||
|
|
||||||
test = lib.mkForce (lib.lazyDerivation {
|
|
||||||
# lazyDerivation improves performance when only passthru items and/or meta are used.
|
|
||||||
derivation = hostPkgs.stdenv.mkDerivation {
|
|
||||||
name = "vm-test-run-${config.name}";
|
|
||||||
|
|
||||||
requiredSystemFeatures = [ "uid-range" ];
|
|
||||||
|
|
||||||
buildCommand = ''
|
|
||||||
mkdir -p $out
|
|
||||||
|
|
||||||
# effectively mute the XMLLogger
|
|
||||||
export LOGFILE=/dev/null
|
|
||||||
|
|
||||||
${config.driver}/bin/nixos-test-driver -o $out
|
|
||||||
'';
|
|
||||||
|
|
||||||
passthru = config.passthru;
|
|
||||||
|
|
||||||
meta = config.meta;
|
|
||||||
};
|
|
||||||
inherit (config) passthru meta;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
{ extraPythonPackages, python3Packages, buildPythonApplication, setuptools, util-linux, systemd }:
|
{
|
||||||
|
extraPythonPackages,
|
||||||
|
python3Packages,
|
||||||
|
buildPythonApplication,
|
||||||
|
setuptools,
|
||||||
|
util-linux,
|
||||||
|
systemd,
|
||||||
|
}:
|
||||||
buildPythonApplication {
|
buildPythonApplication {
|
||||||
pname = "test-driver";
|
pname = "test-driver";
|
||||||
version = "0.0.1";
|
version = "0.0.1";
|
||||||
propagatedBuildInputs = [ util-linux systemd ] ++ extraPythonPackages python3Packages;
|
propagatedBuildInputs = [
|
||||||
|
util-linux
|
||||||
|
systemd
|
||||||
|
] ++ extraPythonPackages python3Packages;
|
||||||
nativeBuildInputs = [ setuptools ];
|
nativeBuildInputs = [ setuptools ];
|
||||||
format = "pyproject";
|
format = "pyproject";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
test:
|
test:
|
||||||
{ pkgs
|
{ pkgs, self, ... }:
|
||||||
, self
|
|
||||||
, ...
|
|
||||||
}:
|
|
||||||
let
|
let
|
||||||
inherit (pkgs) lib;
|
inherit (pkgs) lib;
|
||||||
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
||||||
in
|
in
|
||||||
(nixos-lib.runTest ({ hostPkgs, ... }: {
|
(nixos-lib.runTest (
|
||||||
hostPkgs = pkgs;
|
{ hostPkgs, ... }:
|
||||||
# speed-up evaluation
|
{
|
||||||
defaults = {
|
hostPkgs = pkgs;
|
||||||
documentation.enable = lib.mkDefault false;
|
# speed-up evaluation
|
||||||
boot.isContainer = true;
|
defaults = {
|
||||||
|
documentation.enable = lib.mkDefault false;
|
||||||
|
boot.isContainer = true;
|
||||||
|
|
||||||
# undo qemu stuff
|
# undo qemu stuff
|
||||||
system.build.initialRamdisk = "";
|
system.build.initialRamdisk = "";
|
||||||
virtualisation.sharedDirectories = lib.mkForce { };
|
virtualisation.sharedDirectories = lib.mkForce { };
|
||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
|
|
||||||
# we have not private networking so far
|
# we have not private networking so far
|
||||||
networking.interfaces = lib.mkForce { };
|
networking.interfaces = lib.mkForce { };
|
||||||
#networking.primaryIPAddress = lib.mkForce null;
|
#networking.primaryIPAddress = lib.mkForce null;
|
||||||
systemd.services.backdoor.enable = false;
|
systemd.services.backdoor.enable = false;
|
||||||
};
|
};
|
||||||
# to accept external dependencies such as disko
|
# to accept external dependencies such as disko
|
||||||
node.specialArgs.self = self;
|
node.specialArgs.self = self;
|
||||||
imports = [
|
imports = [
|
||||||
test
|
test
|
||||||
./container-driver/module.nix
|
./container-driver/module.nix
|
||||||
];
|
];
|
||||||
})).config.result
|
}
|
||||||
|
)).config.result
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
test:
|
test:
|
||||||
{ pkgs
|
{ pkgs, self, ... }:
|
||||||
, self
|
|
||||||
, ...
|
|
||||||
}:
|
|
||||||
let
|
let
|
||||||
inherit (pkgs) lib;
|
inherit (pkgs) lib;
|
||||||
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
nixos-lib = import (pkgs.path + "/nixos/lib") { };
|
||||||
|
@ -1,35 +1,48 @@
|
|||||||
{ self, runCommand, check-jsonschema, pkgs, lib, ... }:
|
{
|
||||||
|
self,
|
||||||
|
runCommand,
|
||||||
|
check-jsonschema,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
clanModules.clanCore = self.nixosModules.clanCore;
|
clanModules.clanCore = self.nixosModules.clanCore;
|
||||||
|
|
||||||
baseModule = {
|
baseModule = {
|
||||||
imports =
|
imports = (import (pkgs.path + "/nixos/modules/module-list.nix")) ++ [
|
||||||
(import (pkgs.path + "/nixos/modules/module-list.nix"))
|
{
|
||||||
++ [{
|
|
||||||
nixpkgs.hostPlatform = "x86_64-linux";
|
nixpkgs.hostPlatform = "x86_64-linux";
|
||||||
clanCore.clanName = "dummy";
|
clanCore.clanName = "dummy";
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
optionsFromModule = module:
|
optionsFromModule =
|
||||||
|
module:
|
||||||
let
|
let
|
||||||
evaled = lib.evalModules {
|
evaled = lib.evalModules {
|
||||||
modules = [ module baseModule ];
|
modules = [
|
||||||
|
module
|
||||||
|
baseModule
|
||||||
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
evaled.options.clan;
|
evaled.options.clan;
|
||||||
|
|
||||||
clanModuleSchemas = lib.mapAttrs (_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)) clanModules;
|
clanModuleSchemas = lib.mapAttrs (
|
||||||
|
_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)
|
||||||
|
) clanModules;
|
||||||
|
|
||||||
mkTest = name: schema: runCommand "schema-${name}" { } ''
|
mkTest =
|
||||||
${check-jsonschema}/bin/check-jsonschema \
|
name: schema:
|
||||||
--check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)}
|
runCommand "schema-${name}" { } ''
|
||||||
touch $out
|
${check-jsonschema}/bin/check-jsonschema \
|
||||||
'';
|
--check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
lib.mapAttrs'
|
lib.mapAttrs' (name: schema: {
|
||||||
(name: schema: {
|
name = "schema-${name}";
|
||||||
name = "schema-${name}";
|
value = mkTest name schema;
|
||||||
value = mkTest name schema;
|
}) clanModuleSchemas
|
||||||
})
|
|
||||||
clanModuleSchemas
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
(import ../lib/test-base.nix) {
|
(import ../lib/test-base.nix) {
|
||||||
name = "secrets";
|
name = "secrets";
|
||||||
|
|
||||||
nodes.machine = { self, config, ... }: {
|
nodes.machine =
|
||||||
imports = [
|
{ self, config, ... }:
|
||||||
(self.nixosModules.clanCore)
|
{
|
||||||
];
|
imports = [ (self.nixosModules.clanCore) ];
|
||||||
environment.etc."secret".source = config.sops.secrets.secret.path;
|
environment.etc."secret".source = config.sops.secrets.secret.path;
|
||||||
environment.etc."group-secret".source = config.sops.secrets.group-secret.path;
|
environment.etc."group-secret".source = config.sops.secrets.group-secret.path;
|
||||||
sops.age.keyFile = ./key.age;
|
sops.age.keyFile = ./key.age;
|
||||||
|
|
||||||
clanCore.clanDir = "${./.}";
|
clanCore.clanDir = "${./.}";
|
||||||
clanCore.machineName = "machine";
|
clanCore.machineName = "machine";
|
||||||
|
|
||||||
networking.hostName = "machine";
|
networking.hostName = "machine";
|
||||||
};
|
};
|
||||||
testScript = ''
|
testScript = ''
|
||||||
machine.succeed("cat /etc/secret >&2")
|
machine.succeed("cat /etc/secret >&2")
|
||||||
machine.succeed("cat /etc/group-secret >&2")
|
machine.succeed("cat /etc/group-secret >&2")
|
||||||
|
@ -1,25 +1,35 @@
|
|||||||
import ../lib/test-base.nix ({ config, pkgs, lib, ... }: {
|
import ../lib/test-base.nix (
|
||||||
name = "wayland-proxy-virtwl";
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
name = "wayland-proxy-virtwl";
|
||||||
|
|
||||||
nodes.machine = { self, ... }: {
|
nodes.machine =
|
||||||
imports = [
|
{ self, ... }:
|
||||||
self.nixosModules.clanCore
|
|
||||||
{
|
{
|
||||||
clanCore.machineName = "machine";
|
imports = [
|
||||||
clanCore.clanDir = ./.;
|
self.nixosModules.clanCore
|
||||||
}
|
{
|
||||||
];
|
clanCore.machineName = "machine";
|
||||||
services.wayland-proxy-virtwl.enable = true;
|
clanCore.clanDir = ./.;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
services.wayland-proxy-virtwl.enable = true;
|
||||||
|
|
||||||
virtualisation.qemu.options = [
|
virtualisation.qemu.options = [
|
||||||
"-vga none -device virtio-gpu-rutabaga,cross-domain=on,hostmem=4G,wsi=headless"
|
"-vga none -device virtio-gpu-rutabaga,cross-domain=on,hostmem=4G,wsi=headless"
|
||||||
];
|
];
|
||||||
|
|
||||||
virtualisation.qemu.package = lib.mkForce pkgs.qemu_kvm;
|
virtualisation.qemu.package = lib.mkForce pkgs.qemu_kvm;
|
||||||
};
|
};
|
||||||
testScript = ''
|
testScript = ''
|
||||||
start_all()
|
start_all()
|
||||||
# use machinectl
|
# use machinectl
|
||||||
machine.succeed("machinectl shell .host ${config.nodes.machine.systemd.package}/bin/systemctl --user start wayland-proxy-virtwl >&2")
|
machine.succeed("machinectl shell .host ${config.nodes.machine.systemd.package}/bin/systemctl --user start wayland-proxy-virtwl >&2")
|
||||||
'';
|
'';
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
(import ../lib/container-test.nix) ({ pkgs, ... }: {
|
(import ../lib/container-test.nix) (
|
||||||
name = "zt-tcp-relay";
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
name = "zt-tcp-relay";
|
||||||
|
|
||||||
nodes.machine = { self, ... }: {
|
nodes.machine =
|
||||||
imports = [
|
{ self, ... }:
|
||||||
self.nixosModules.clanCore
|
|
||||||
self.clanModules.zt-tcp-relay
|
|
||||||
{
|
{
|
||||||
clanCore.machineName = "machine";
|
imports = [
|
||||||
clanCore.clanDir = ./.;
|
self.nixosModules.clanCore
|
||||||
}
|
self.clanModules.zt-tcp-relay
|
||||||
];
|
{
|
||||||
};
|
clanCore.machineName = "machine";
|
||||||
testScript = ''
|
clanCore.clanDir = ./.;
|
||||||
start_all()
|
}
|
||||||
machine.wait_for_unit("zt-tcp-relay.service")
|
];
|
||||||
out = machine.succeed("${pkgs.netcat}/bin/nc -z -v localhost 4443")
|
};
|
||||||
print(out)
|
testScript = ''
|
||||||
'';
|
start_all()
|
||||||
})
|
machine.wait_for_unit("zt-tcp-relay.service")
|
||||||
|
out = machine.succeed("${pkgs.netcat}/bin/nc -z -v localhost 4443")
|
||||||
|
print(out)
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -1,69 +1,88 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.clan.borgbackup;
|
cfg = config.clan.borgbackup;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.clan.borgbackup.destinations = lib.mkOption {
|
options.clan.borgbackup.destinations = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
|
type = lib.types.attrsOf (
|
||||||
options = {
|
lib.types.submodule (
|
||||||
name = lib.mkOption {
|
{ name, ... }:
|
||||||
type = lib.types.str;
|
{
|
||||||
default = name;
|
options = {
|
||||||
description = "the name of the backup job";
|
name = lib.mkOption {
|
||||||
};
|
type = lib.types.str;
|
||||||
repo = lib.mkOption {
|
default = name;
|
||||||
type = lib.types.str;
|
description = "the name of the backup job";
|
||||||
description = "the borgbackup repository to backup to";
|
};
|
||||||
};
|
repo = lib.mkOption {
|
||||||
rsh = lib.mkOption {
|
type = lib.types.str;
|
||||||
type = lib.types.str;
|
description = "the borgbackup repository to backup to";
|
||||||
default = "ssh -i ${config.clanCore.secrets.borgbackup.secrets."borgbackup.ssh".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
|
};
|
||||||
description = "the rsh to use for the backup";
|
rsh = lib.mkOption {
|
||||||
};
|
type = lib.types.str;
|
||||||
|
default = "ssh -i ${
|
||||||
};
|
config.clanCore.secrets.borgbackup.secrets."borgbackup.ssh".path
|
||||||
}));
|
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
|
||||||
|
description = "the rsh to use for the backup";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
default = { };
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
destinations where the machine should be backuped to
|
destinations where the machine should be backuped to
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
imports = [ (lib.mkRemovedOptionModule [ "clan" "borgbackup" "enable" ] "Just define clan.borgbackup.destinations to enable it") ];
|
imports = [
|
||||||
|
(lib.mkRemovedOptionModule [
|
||||||
|
"clan"
|
||||||
|
"borgbackup"
|
||||||
|
"enable"
|
||||||
|
] "Just define clan.borgbackup.destinations to enable it")
|
||||||
|
];
|
||||||
|
|
||||||
config = lib.mkIf (cfg.destinations != { }) {
|
config = lib.mkIf (cfg.destinations != { }) {
|
||||||
services.borgbackup.jobs = lib.mapAttrs
|
services.borgbackup.jobs = lib.mapAttrs (_: dest: {
|
||||||
(_: dest: {
|
paths = lib.flatten (map (state: state.folders) (lib.attrValues config.clanCore.state));
|
||||||
paths = lib.flatten (map (state: state.folders) (lib.attrValues config.clanCore.state));
|
exclude = [ "*.pyc" ];
|
||||||
exclude = [ "*.pyc" ];
|
repo = dest.repo;
|
||||||
repo = dest.repo;
|
environment.BORG_RSH = dest.rsh;
|
||||||
environment.BORG_RSH = dest.rsh;
|
compression = "auto,zstd";
|
||||||
compression = "auto,zstd";
|
startAt = "*-*-* 01:00:00";
|
||||||
startAt = "*-*-* 01:00:00";
|
persistentTimer = true;
|
||||||
persistentTimer = true;
|
preHook = ''
|
||||||
preHook = ''
|
set -x
|
||||||
set -x
|
'';
|
||||||
'';
|
|
||||||
|
|
||||||
encryption = {
|
encryption = {
|
||||||
mode = "repokey";
|
mode = "repokey";
|
||||||
passCommand = "cat ${config.clanCore.secrets.borgbackup.secrets."borgbackup.repokey".path}";
|
passCommand = "cat ${config.clanCore.secrets.borgbackup.secrets."borgbackup.repokey".path}";
|
||||||
};
|
};
|
||||||
|
|
||||||
prune.keep = {
|
prune.keep = {
|
||||||
within = "1d"; # Keep all archives from the last day
|
within = "1d"; # Keep all archives from the last day
|
||||||
daily = 7;
|
daily = 7;
|
||||||
weekly = 4;
|
weekly = 4;
|
||||||
monthly = 0;
|
monthly = 0;
|
||||||
};
|
};
|
||||||
})
|
}) cfg.destinations;
|
||||||
cfg.destinations;
|
|
||||||
|
|
||||||
clanCore.secrets.borgbackup = {
|
clanCore.secrets.borgbackup = {
|
||||||
facts."borgbackup.ssh.pub" = { };
|
facts."borgbackup.ssh.pub" = { };
|
||||||
secrets."borgbackup.ssh" = { };
|
secrets."borgbackup.ssh" = { };
|
||||||
secrets."borgbackup.repokey" = { };
|
secrets."borgbackup.repokey" = { };
|
||||||
generator.path = [ pkgs.openssh pkgs.coreutils pkgs.xkcdpass ];
|
generator.path = [
|
||||||
|
pkgs.openssh
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.xkcdpass
|
||||||
|
];
|
||||||
generator.script = ''
|
generator.script = ''
|
||||||
ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh
|
ssh-keygen -t ed25519 -N "" -f "$secrets"/borgbackup.ssh
|
||||||
mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub
|
mv "$secrets"/borgbackup.ssh.pub "$facts"/borgbackup.ssh.pub
|
||||||
@ -75,8 +94,9 @@ in
|
|||||||
# TODO list needs to run locally or on the remote machine
|
# TODO list needs to run locally or on the remote machine
|
||||||
list = ''
|
list = ''
|
||||||
# we need yes here to skip the changed url verification
|
# we need yes here to skip the changed url verification
|
||||||
${lib.concatMapStringsSep "\n" (dest: ''yes y | borg-job-${dest.name} list --json | jq -r '. + {"job-name": "${dest.name}"}' '')
|
${lib.concatMapStringsSep "\n" (
|
||||||
(lib.attrValues cfg.destinations)}
|
dest: ''yes y | borg-job-${dest.name} list --json | jq -r '. + {"job-name": "${dest.name}"}' ''
|
||||||
|
) (lib.attrValues cfg.destinations)}
|
||||||
'';
|
'';
|
||||||
create = ''
|
create = ''
|
||||||
${lib.concatMapStringsSep "\n" (dest: ''
|
${lib.concatMapStringsSep "\n" (dest: ''
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ config, pkgs, ... }: {
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
|
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
|
||||||
environment.systemPackages = [ pkgs.deltachat-desktop ];
|
environment.systemPackages = [ pkgs.deltachat-desktop ];
|
||||||
|
|
||||||
@ -134,9 +135,7 @@
|
|||||||
storage &local_mailboxes
|
storage &local_mailboxes
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
ensureAccounts = [
|
ensureAccounts = [ "user@${domain}" ];
|
||||||
"user@${domain}"
|
|
||||||
];
|
|
||||||
ensureCredentials = {
|
ensureCredentials = {
|
||||||
"user@${domain}".passwordFile = pkgs.writeText "dummy" "foobar";
|
"user@${domain}".passwordFile = pkgs.writeText "dummy" "foobar";
|
||||||
};
|
};
|
||||||
|
@ -41,4 +41,3 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ inputs, ... }: {
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
flake.clanModules = {
|
flake.clanModules = {
|
||||||
diskLayouts = {
|
diskLayouts = {
|
||||||
imports = [
|
imports = [
|
||||||
|
@ -1,4 +1 @@
|
|||||||
_:
|
_: { fonts.enableDefaultPackages = true; }
|
||||||
{
|
|
||||||
fonts.enableDefaultPackages = true;
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{ config
|
{
|
||||||
, pkgs
|
config,
|
||||||
, lib
|
pkgs,
|
||||||
, ...
|
lib,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
# Integration can be improved, if the following issues get implemented:
|
# Integration can be improved, if the following issues get implemented:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ pkgs, ... }: {
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
hardware.opengl.enable = true;
|
hardware.opengl.enable = true;
|
||||||
environment.systemPackages = [ pkgs.moonlight-qt ];
|
environment.systemPackages = [ pkgs.moonlight-qt ];
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
{ config, pkgs, ... }: {
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
|
|
||||||
services.openssh.hostKeys = [{
|
services.openssh.hostKeys = [
|
||||||
path = config.clanCore.secrets.openssh.secrets."ssh.id_ed25519".path;
|
{
|
||||||
type = "ed25519";
|
path = config.clanCore.secrets.openssh.secrets."ssh.id_ed25519".path;
|
||||||
}];
|
type = "ed25519";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
clanCore.secrets.openssh = {
|
clanCore.secrets.openssh = {
|
||||||
secrets."ssh.id_ed25519" = { };
|
secrets."ssh.id_ed25519" = { };
|
||||||
facts."ssh.id_ed25519.pub" = { };
|
facts."ssh.id_ed25519.pub" = { };
|
||||||
generator.path = [ pkgs.coreutils pkgs.openssh ];
|
generator.path = [
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.openssh
|
||||||
|
];
|
||||||
generator.script = ''
|
generator.script = ''
|
||||||
ssh-keygen -t ed25519 -N "" -f $secrets/ssh.id_ed25519
|
ssh-keygen -t ed25519 -N "" -f $secrets/ssh.id_ed25519
|
||||||
mv $secrets/ssh.id_ed25519.pub $facts/ssh.id_ed25519.pub
|
mv $secrets/ssh.id_ed25519.pub $facts/ssh.id_ed25519.pub
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{ pkgs, options, ... }:
|
{ pkgs, options, ... }:
|
||||||
let
|
let
|
||||||
apps = pkgs.writeText "apps.json" (builtins.toJSON
|
apps = pkgs.writeText "apps.json" (
|
||||||
{
|
builtins.toJSON {
|
||||||
env = {
|
env = {
|
||||||
PATH = "$(PATH):$(HOME)/.local/bin:/run/current-system/sw/bin";
|
PATH = "$(PATH):$(HOME)/.local/bin:/run/current-system/sw/bin";
|
||||||
};
|
};
|
||||||
@ -22,13 +22,12 @@ let
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "Steam Big Picture";
|
name = "Steam Big Picture";
|
||||||
detached = [
|
detached = [ "setsid steam steam://open/bigpicture" ];
|
||||||
"setsid steam steam://open/bigpicture"
|
|
||||||
];
|
|
||||||
image-path = "steam.png";
|
image-path = "steam.png";
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
|
);
|
||||||
sunshineConfiguration = pkgs.writeText "sunshine.conf" ''
|
sunshineConfiguration = pkgs.writeText "sunshine.conf" ''
|
||||||
address_family = both
|
address_family = both
|
||||||
channels = 5
|
channels = 5
|
||||||
@ -78,11 +77,9 @@ in
|
|||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.sunshine
|
pkgs.sunshine
|
||||||
(pkgs.writers.writeDashBin "sun" ''
|
(pkgs.writers.writeDashBin "sun" ''
|
||||||
${pkgs.sunshine}/bin/sunshine -1 ${
|
${pkgs.sunshine}/bin/sunshine -1 ${pkgs.writeText "sunshine.conf" ''
|
||||||
pkgs.writeText "sunshine.conf" ''
|
address_family = both
|
||||||
address_family = both
|
''} "$@"
|
||||||
''
|
|
||||||
} "$@"
|
|
||||||
'')
|
'')
|
||||||
# Create a dummy account, for easier setup,
|
# Create a dummy account, for easier setup,
|
||||||
# don't use this account in actual production yet.
|
# don't use this account in actual production yet.
|
||||||
@ -113,11 +110,7 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [ "d '/var/lib/sunshine' 0770 'user' 'users' - -" ];
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d '/var/lib/sunshine' 0770 'user' 'users' - -"
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
systemd.user.services.sunshine = {
|
systemd.user.services.sunshine = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@ -128,9 +121,7 @@ in
|
|||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
RestartSec = "5s";
|
RestartSec = "5s";
|
||||||
ReadWritePaths = [
|
ReadWritePaths = [ "/var/lib/sunshine" ];
|
||||||
"/var/lib/sunshine"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
wantedBy = [ "graphical-session.target" ];
|
wantedBy = [ "graphical-session.target" ];
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{ config
|
{
|
||||||
, pkgs
|
config,
|
||||||
, lib
|
pkgs,
|
||||||
, ...
|
lib,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
options.clan.syncthing = {
|
options.clan.syncthing = {
|
||||||
@ -53,9 +54,9 @@
|
|||||||
|
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion =
|
assertion = lib.all (
|
||||||
lib.all (attr: builtins.hasAttr attr config.services.syncthing.settings.folders)
|
attr: builtins.hasAttr attr config.services.syncthing.settings.folders
|
||||||
config.clan.syncthing.autoShares;
|
) config.clan.syncthing.autoShares;
|
||||||
message = ''
|
message = ''
|
||||||
Syncthing: If you want to AutoShare a folder, you need to have it configured on the sharing device.
|
Syncthing: If you want to AutoShare a folder, you need to have it configured on the sharing device.
|
||||||
'';
|
'';
|
||||||
@ -80,12 +81,8 @@
|
|||||||
|
|
||||||
group = "syncthing";
|
group = "syncthing";
|
||||||
|
|
||||||
key =
|
key = lib.mkDefault config.clan.secrets.syncthing.secrets."syncthing.key".path or null;
|
||||||
lib.mkDefault
|
cert = lib.mkDefault config.clan.secrets.syncthing.secrets."syncthing.cert".path or null;
|
||||||
config.clan.secrets.syncthing.secrets."syncthing.key".path or null;
|
|
||||||
cert =
|
|
||||||
lib.mkDefault
|
|
||||||
config.clan.secrets.syncthing.secrets."syncthing.cert".path or null;
|
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
options = {
|
options = {
|
||||||
@ -127,47 +124,33 @@
|
|||||||
set -x
|
set -x
|
||||||
# query pending deviceID's
|
# query pending deviceID's
|
||||||
APIKEY=$(cat ${apiKey})
|
APIKEY=$(cat ${apiKey})
|
||||||
PENDING=$(${
|
PENDING=$(${lib.getExe pkgs.curl} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${getPendingDevices})
|
||||||
lib.getExe pkgs.curl
|
|
||||||
} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${getPendingDevices})
|
|
||||||
PENDING=$(echo $PENDING | ${lib.getExe pkgs.jq} keys[])
|
PENDING=$(echo $PENDING | ${lib.getExe pkgs.jq} keys[])
|
||||||
|
|
||||||
# accept pending deviceID's
|
# accept pending deviceID's
|
||||||
for ID in $PENDING;do
|
for ID in $PENDING;do
|
||||||
${
|
${lib.getExe pkgs.curl} -X POST -d "{\"deviceId\": $ID}" -H "Content-Type: application/json" -H "X-API-Key: $APIKEY" ${baseAddress}${postNewDevice}
|
||||||
lib.getExe pkgs.curl
|
|
||||||
} -X POST -d "{\"deviceId\": $ID}" -H "Content-Type: application/json" -H "X-API-Key: $APIKEY" ${baseAddress}${postNewDevice}
|
|
||||||
|
|
||||||
# get all shared folders by their ID
|
# get all shared folders by their ID
|
||||||
for folder in ${builtins.toString config.clan.syncthing.autoShares}; do
|
for folder in ${builtins.toString config.clan.syncthing.autoShares}; do
|
||||||
SHARED_IDS=$(${
|
SHARED_IDS=$(${lib.getExe pkgs.curl} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder" | ${lib.getExe pkgs.jq} ."devices")
|
||||||
lib.getExe pkgs.curl
|
PATCHED_IDS=$(echo $SHARED_IDS | ${lib.getExe pkgs.jq} ".+= [{\"deviceID\": $ID, \"introducedBy\": \"\", \"encryptionPassword\": \"\"}]")
|
||||||
} -X GET -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder" | ${
|
${lib.getExe pkgs.curl} -X PATCH -d "{\"devices\": $PATCHED_IDS}" -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder"
|
||||||
lib.getExe pkgs.jq
|
|
||||||
} ."devices")
|
|
||||||
PATCHED_IDS=$(echo $SHARED_IDS | ${
|
|
||||||
lib.getExe pkgs.jq
|
|
||||||
} ".+= [{\"deviceID\": $ID, \"introducedBy\": \"\", \"encryptionPassword\": \"\"}]")
|
|
||||||
${
|
|
||||||
lib.getExe pkgs.curl
|
|
||||||
} -X PATCH -d "{\"devices\": $PATCHED_IDS}" -H "X-API-Key: $APIKEY" ${baseAddress}${SharedFolderById}"$folder"
|
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.timers.syncthing-auto-accept =
|
systemd.timers.syncthing-auto-accept = lib.mkIf config.clan.syncthing.autoAcceptDevices {
|
||||||
lib.mkIf config.clan.syncthing.autoAcceptDevices
|
description = "Syncthing Auto Accept";
|
||||||
{
|
|
||||||
description = "Syncthing Auto Accept";
|
|
||||||
|
|
||||||
wantedBy = [ "syncthing-auto-accept.service" ];
|
wantedBy = [ "syncthing-auto-accept.service" ];
|
||||||
|
|
||||||
timerConfig = {
|
timerConfig = {
|
||||||
OnActiveSec = lib.mkDefault 60;
|
OnActiveSec = lib.mkDefault 60;
|
||||||
OnUnitActiveSec = lib.mkDefault 60;
|
OnUnitActiveSec = lib.mkDefault 60;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.syncthing-init-api-key =
|
systemd.services.syncthing-init-api-key =
|
||||||
let
|
let
|
||||||
@ -182,9 +165,7 @@
|
|||||||
set -efu pipefail
|
set -efu pipefail
|
||||||
|
|
||||||
APIKEY=$(cat ${apiKey})
|
APIKEY=$(cat ${apiKey})
|
||||||
${
|
${lib.getExe pkgs.gnused} -i "s/<apikey>.*<\/apikey>/<apikey>$APIKEY<\/apikey>/" /var/lib/syncthing/config.xml
|
||||||
lib.getExe pkgs.gnused
|
|
||||||
} -i "s/<apikey>.*<\/apikey>/<apikey>$APIKEY<\/apikey>/" /var/lib/syncthing/config.xml
|
|
||||||
# sudo systemctl restart syncthing.service
|
# sudo systemctl restart syncthing.service
|
||||||
systemctl restart syncthing.service
|
systemctl restart syncthing.service
|
||||||
'';
|
'';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{ pkgs
|
{
|
||||||
, lib
|
pkgs,
|
||||||
, config
|
lib,
|
||||||
, ...
|
config,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
options.clan.services.waypipe = {
|
options.clan.services.waypipe = {
|
||||||
@ -49,7 +50,10 @@
|
|||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
uid = 1000;
|
uid = 1000;
|
||||||
password = "";
|
password = "";
|
||||||
extraGroups = [ "wheel" "video" ];
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
"video"
|
||||||
|
];
|
||||||
shell = "/run/current-system/sw/bin/bash";
|
shell = "/run/current-system/sw/bin/bash";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
{ pkgs, lib, config, ... }: {
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
options.clan.zt-tcp-relay = {
|
options.clan.zt-tcp-relay = {
|
||||||
port = lib.mkOption {
|
port = lib.mkOption {
|
||||||
type = lib.types.port;
|
type = lib.types.port;
|
||||||
@ -13,7 +19,9 @@
|
|||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${pkgs.callPackage ../pkgs/zt-tcp-relay {}}/bin/zt-tcp-relay --listen [::]:${builtins.toString config.clan.zt-tcp-relay.port}";
|
ExecStart = "${
|
||||||
|
pkgs.callPackage ../pkgs/zt-tcp-relay { }
|
||||||
|
}/bin/zt-tcp-relay --listen [::]:${builtins.toString config.clan.zt-tcp-relay.port}";
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
RestartSec = "5";
|
RestartSec = "5";
|
||||||
dynamicUsers = true;
|
dynamicUsers = true;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
perSystem =
|
perSystem =
|
||||||
{ pkgs
|
{
|
||||||
, self'
|
pkgs,
|
||||||
, lib
|
self',
|
||||||
, ...
|
lib,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
python3 = pkgs.python3;
|
python3 = pkgs.python3;
|
||||||
@ -20,9 +21,7 @@
|
|||||||
ps.pygobject3
|
ps.pygobject3
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
linuxOnlyPackages = lib.optionals pkgs.stdenv.isLinux [
|
linuxOnlyPackages = lib.optionals pkgs.stdenv.isLinux [ pkgs.xdg-utils ];
|
||||||
pkgs.xdg-utils
|
|
||||||
];
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
devShells.python = pkgs.mkShell {
|
devShells.python = pkgs.mkShell {
|
||||||
|
16
devShell.nix
16
devShell.nix
@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
perSystem =
|
perSystem =
|
||||||
{ pkgs
|
{
|
||||||
, self'
|
pkgs,
|
||||||
, config
|
self',
|
||||||
, ...
|
config,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
writers = pkgs.callPackage ./pkgs/builders/script-writers.nix { };
|
writers = pkgs.callPackage ./pkgs/builders/script-writers.nix { };
|
||||||
@ -16,10 +17,9 @@
|
|||||||
# A python program to switch between dev-shells
|
# A python program to switch between dev-shells
|
||||||
# usage: select-shell shell-name
|
# usage: select-shell shell-name
|
||||||
# the currently enabled dev-shell gets stored in ./.direnv/selected-shell
|
# the currently enabled dev-shell gets stored in ./.direnv/selected-shell
|
||||||
select-shell = writers.writePython3Bin "select-shell"
|
select-shell = writers.writePython3Bin "select-shell" {
|
||||||
{
|
flakeIgnore = [ "E501" ];
|
||||||
flakeIgnore = [ "E501" ];
|
} ./pkgs/scripts/select-shell.py;
|
||||||
} ./pkgs/scripts/select-shell.py;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
|
70
flake.nix
70
flake.nix
@ -2,7 +2,9 @@
|
|||||||
description = "clan.lol base operating system";
|
description = "clan.lol base operating system";
|
||||||
|
|
||||||
nixConfig.extra-substituters = [ "https://cache.clan.lol" ];
|
nixConfig.extra-substituters = [ "https://cache.clan.lol" ];
|
||||||
nixConfig.extra-trusted-public-keys = [ "cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28=" ];
|
nixConfig.extra-trusted-public-keys = [
|
||||||
|
"cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28="
|
||||||
|
];
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small";
|
||||||
@ -20,44 +22,42 @@
|
|||||||
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs @ { flake-parts, ... }:
|
outputs =
|
||||||
flake-parts.lib.mkFlake { inherit inputs; } ({ lib, ... }: {
|
inputs@{ flake-parts, ... }:
|
||||||
systems = [
|
flake-parts.lib.mkFlake { inherit inputs; } (
|
||||||
"x86_64-linux"
|
{ lib, ... }:
|
||||||
"aarch64-linux"
|
{
|
||||||
"aarch64-darwin"
|
systems = [
|
||||||
];
|
"x86_64-linux"
|
||||||
imports = [
|
"aarch64-linux"
|
||||||
./checks/flake-module.nix
|
"aarch64-darwin"
|
||||||
./devShell.nix
|
];
|
||||||
./devShell-python.nix
|
imports = [
|
||||||
./formatter.nix
|
./checks/flake-module.nix
|
||||||
./templates/flake-module.nix
|
./devShell.nix
|
||||||
./clanModules/flake-module.nix
|
./devShell-python.nix
|
||||||
|
./formatter.nix
|
||||||
|
./templates/flake-module.nix
|
||||||
|
./clanModules/flake-module.nix
|
||||||
|
|
||||||
./pkgs/flake-module.nix
|
./pkgs/flake-module.nix
|
||||||
|
|
||||||
./lib/flake-module.nix
|
./lib/flake-module.nix
|
||||||
./nixosModules/flake-module.nix
|
./nixosModules/flake-module.nix
|
||||||
{
|
{
|
||||||
options.flake = flake-parts.lib.mkSubmoduleOptions {
|
options.flake = flake-parts.lib.mkSubmoduleOptions {
|
||||||
clanInternals = lib.mkOption {
|
clanInternals = lib.mkOption {
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
all-machines-json = lib.mkOption {
|
all-machines-json = lib.mkOption { type = lib.types.attrsOf lib.types.str; };
|
||||||
type = lib.types.attrsOf lib.types.str;
|
machines = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
|
||||||
};
|
machinesFunc = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
|
||||||
machines = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified);
|
|
||||||
};
|
|
||||||
machinesFunc = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
];
|
||||||
];
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,47 @@
|
|||||||
{ lib
|
{ lib, inputs, ... }:
|
||||||
, inputs
|
{
|
||||||
, ...
|
imports = [ inputs.treefmt-nix.flakeModule ];
|
||||||
}: {
|
perSystem =
|
||||||
imports = [
|
{ self', pkgs, ... }:
|
||||||
inputs.treefmt-nix.flakeModule
|
{
|
||||||
];
|
treefmt.projectRootFile = "flake.nix";
|
||||||
perSystem = { self', pkgs, ... }: {
|
treefmt.programs.shellcheck.enable = true;
|
||||||
treefmt.projectRootFile = "flake.nix";
|
|
||||||
treefmt.flakeCheck = true;
|
|
||||||
treefmt.flakeFormatter = true;
|
|
||||||
treefmt.programs.shellcheck.enable = true;
|
|
||||||
|
|
||||||
treefmt.programs.mypy.enable = true;
|
treefmt.programs.mypy.enable = true;
|
||||||
treefmt.programs.mypy.directories = {
|
treefmt.programs.mypy.directories = {
|
||||||
"pkgs/clan-cli".extraPythonPackages = self'.packages.clan-cli.pytestDependencies;
|
"pkgs/clan-cli".extraPythonPackages = self'.packages.clan-cli.pytestDependencies;
|
||||||
"pkgs/clan-vm-manager".extraPythonPackages = self'.packages.clan-vm-manager.propagatedBuildInputs;
|
"pkgs/clan-vm-manager".extraPythonPackages = self'.packages.clan-vm-manager.propagatedBuildInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
treefmt.settings.formatter.nix = {
|
treefmt.settings.formatter.nix = {
|
||||||
command = "sh";
|
command = "sh";
|
||||||
options = [
|
options = [
|
||||||
"-eucx"
|
"-eucx"
|
||||||
''
|
''
|
||||||
# First deadnix
|
# First deadnix
|
||||||
${lib.getExe pkgs.deadnix} --edit "$@"
|
${lib.getExe pkgs.deadnix} --edit "$@"
|
||||||
# Then nixpkgs-fmt
|
# Then nixpkgs-fmt
|
||||||
${lib.getExe pkgs.nixpkgs-fmt} "$@"
|
${lib.getExe pkgs.nixfmt-rfc-style} "$@"
|
||||||
''
|
''
|
||||||
"--" # this argument is ignored by bash
|
"--" # this argument is ignored by bash
|
||||||
];
|
];
|
||||||
includes = [ "*.nix" ];
|
includes = [ "*.nix" ];
|
||||||
excludes = [
|
excludes = [
|
||||||
# Was copied from nixpkgs. Keep diff minimal to simplify upstreaming.
|
# Was copied from nixpkgs. Keep diff minimal to simplify upstreaming.
|
||||||
"pkgs/builders/script-writers.nix"
|
"pkgs/builders/script-writers.nix"
|
||||||
];
|
];
|
||||||
|
};
|
||||||
|
treefmt.settings.formatter.python = {
|
||||||
|
command = "sh";
|
||||||
|
options = [
|
||||||
|
"-eucx"
|
||||||
|
''
|
||||||
|
${lib.getExe pkgs.ruff} --fix "$@"
|
||||||
|
${lib.getExe pkgs.ruff} format "$@"
|
||||||
|
''
|
||||||
|
"--" # this argument is ignored by bash
|
||||||
|
];
|
||||||
|
includes = [ "*.py" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
treefmt.settings.formatter.python = {
|
|
||||||
command = "sh";
|
|
||||||
options = [
|
|
||||||
"-eucx"
|
|
||||||
''
|
|
||||||
${lib.getExe pkgs.ruff} --fix "$@"
|
|
||||||
${lib.getExe pkgs.ruff} format "$@"
|
|
||||||
''
|
|
||||||
"--" # this argument is ignored by bash
|
|
||||||
];
|
|
||||||
includes = [ "*.py" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,66 +1,80 @@
|
|||||||
{ clan-core, nixpkgs, lib }:
|
{
|
||||||
{ directory # The directory containing the machines subdirectory
|
clan-core,
|
||||||
, specialArgs ? { } # Extra arguments to pass to nixosSystem i.e. useful to make self available
|
nixpkgs,
|
||||||
, machines ? { } # allows to include machine-specific modules i.e. machines.${name} = { ... }
|
lib,
|
||||||
, clanName # Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.
|
}:
|
||||||
, clanIcon ? null # A path to an icon to be used for the clan, should be the same for all machines
|
{
|
||||||
, pkgsForSystem ? (_system: null) # A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system.
|
directory, # The directory containing the machines subdirectory
|
||||||
# This improves performance, but all nipxkgs.* options will be ignored.
|
specialArgs ? { }, # Extra arguments to pass to nixosSystem i.e. useful to make self available
|
||||||
|
machines ? { }, # allows to include machine-specific modules i.e. machines.${name} = { ... }
|
||||||
|
clanName, # Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.
|
||||||
|
clanIcon ? null, # A path to an icon to be used for the clan, should be the same for all machines
|
||||||
|
pkgsForSystem ? (_system: null), # A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system.
|
||||||
|
# This improves performance, but all nipxkgs.* options will be ignored.
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") (builtins.readDir (directory + /machines));
|
machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") (
|
||||||
|
builtins.readDir (directory + /machines)
|
||||||
|
);
|
||||||
|
|
||||||
machineSettings = machineName:
|
machineSettings =
|
||||||
|
machineName:
|
||||||
# CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily
|
# CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily
|
||||||
# This is useful for doing a dry-run before writing changes into the settings.json
|
# This is useful for doing a dry-run before writing changes into the settings.json
|
||||||
# Using CLAN_MACHINE_SETTINGS_FILE requires passing --impure to nix eval
|
# Using CLAN_MACHINE_SETTINGS_FILE requires passing --impure to nix eval
|
||||||
if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != ""
|
if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" then
|
||||||
then builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE"))
|
builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE"))
|
||||||
else
|
else
|
||||||
lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json")
|
lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json") (
|
||||||
(builtins.fromJSON
|
builtins.fromJSON (builtins.readFile (directory + /machines/${machineName}/settings.json))
|
||||||
(builtins.readFile (directory + /machines/${machineName}/settings.json)));
|
);
|
||||||
|
|
||||||
# Read additional imports specified via a config option in settings.json
|
# Read additional imports specified via a config option in settings.json
|
||||||
# This is not an infinite recursion, because the imports are discovered here
|
# This is not an infinite recursion, because the imports are discovered here
|
||||||
# before calling evalModules.
|
# before calling evalModules.
|
||||||
# It is still useful to have the imports as an option, as this allows for type
|
# It is still useful to have the imports as an option, as this allows for type
|
||||||
# checking and easy integration with the config frontend(s)
|
# checking and easy integration with the config frontend(s)
|
||||||
machineImports = machineSettings:
|
machineImports =
|
||||||
map
|
machineSettings: map (module: clan-core.clanModules.${module}) (machineSettings.clanImports or [ ]);
|
||||||
(module: clan-core.clanModules.${module})
|
|
||||||
(machineSettings.clanImports or [ ]);
|
|
||||||
|
|
||||||
# TODO: remove default system once we have a hardware-config mechanism
|
# TODO: remove default system once we have a hardware-config mechanism
|
||||||
nixosConfiguration = { system ? "x86_64-linux", name, pkgs ? null, extraConfig ? { } }: nixpkgs.lib.nixosSystem {
|
nixosConfiguration =
|
||||||
modules =
|
{
|
||||||
let
|
system ? "x86_64-linux",
|
||||||
settings = machineSettings name;
|
name,
|
||||||
in
|
pkgs ? null,
|
||||||
(machineImports settings)
|
extraConfig ? { },
|
||||||
++ [
|
}:
|
||||||
settings
|
nixpkgs.lib.nixosSystem {
|
||||||
clan-core.nixosModules.clanCore
|
modules =
|
||||||
extraConfig
|
let
|
||||||
(machines.${name} or { })
|
settings = machineSettings name;
|
||||||
({
|
in
|
||||||
clanCore.clanName = clanName;
|
(machineImports settings)
|
||||||
clanCore.clanIcon = clanIcon;
|
++ [
|
||||||
clanCore.clanDir = directory;
|
settings
|
||||||
clanCore.machineName = name;
|
clan-core.nixosModules.clanCore
|
||||||
nixpkgs.hostPlatform = lib.mkDefault system;
|
extraConfig
|
||||||
|
(machines.${name} or { })
|
||||||
|
(
|
||||||
|
{
|
||||||
|
clanCore.clanName = clanName;
|
||||||
|
clanCore.clanIcon = clanIcon;
|
||||||
|
clanCore.clanDir = directory;
|
||||||
|
clanCore.machineName = name;
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault system;
|
||||||
|
|
||||||
# speeds up nix commands by using the nixpkgs from the host system (especially useful in VMs)
|
# speeds up nix commands by using the nixpkgs from the host system (especially useful in VMs)
|
||||||
nix.registry.nixpkgs.to = {
|
nix.registry.nixpkgs.to = {
|
||||||
type = "path";
|
type = "path";
|
||||||
path = lib.mkDefault nixpkgs;
|
path = lib.mkDefault nixpkgs;
|
||||||
};
|
};
|
||||||
} // lib.optionalAttrs (pkgs != null) {
|
}
|
||||||
nixpkgs.pkgs = lib.mkForce pkgs;
|
// lib.optionalAttrs (pkgs != null) { nixpkgs.pkgs = lib.mkForce pkgs; }
|
||||||
})
|
)
|
||||||
];
|
];
|
||||||
inherit specialArgs;
|
inherit specialArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
allMachines = machinesDirs // machines;
|
allMachines = machinesDirs // machines;
|
||||||
|
|
||||||
@ -77,27 +91,38 @@ let
|
|||||||
# This instantiates nixos for each system that we support:
|
# This instantiates nixos for each system that we support:
|
||||||
# configPerSystem = <system>.<machine>.nixosConfiguration
|
# configPerSystem = <system>.<machine>.nixosConfiguration
|
||||||
# We need this to build nixos secret generators for each system
|
# We need this to build nixos secret generators for each system
|
||||||
configsPerSystem = builtins.listToAttrs
|
configsPerSystem = builtins.listToAttrs (
|
||||||
(builtins.map
|
builtins.map (
|
||||||
(system: lib.nameValuePair system
|
system:
|
||||||
(lib.mapAttrs
|
lib.nameValuePair system (
|
||||||
(name: _: nixosConfiguration {
|
lib.mapAttrs (
|
||||||
|
name: _:
|
||||||
|
nixosConfiguration {
|
||||||
inherit name system;
|
inherit name system;
|
||||||
pkgs = pkgsForSystem system;
|
pkgs = pkgsForSystem system;
|
||||||
})
|
}
|
||||||
allMachines))
|
) allMachines
|
||||||
supportedSystems);
|
)
|
||||||
|
) supportedSystems
|
||||||
|
);
|
||||||
|
|
||||||
configsFuncPerSystem = builtins.listToAttrs
|
configsFuncPerSystem = builtins.listToAttrs (
|
||||||
(builtins.map
|
builtins.map (
|
||||||
(system: lib.nameValuePair system
|
system:
|
||||||
(lib.mapAttrs
|
lib.nameValuePair system (
|
||||||
(name: _: args: nixosConfiguration (args // {
|
lib.mapAttrs (
|
||||||
inherit name system;
|
name: _: args:
|
||||||
pkgs = pkgsForSystem system;
|
nixosConfiguration (
|
||||||
}))
|
args
|
||||||
allMachines))
|
// {
|
||||||
supportedSystems);
|
inherit name system;
|
||||||
|
pkgs = pkgsForSystem system;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) allMachines
|
||||||
|
)
|
||||||
|
) supportedSystems
|
||||||
|
);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
inherit nixosConfigurations;
|
inherit nixosConfigurations;
|
||||||
@ -105,8 +130,11 @@ in
|
|||||||
clanInternals = {
|
clanInternals = {
|
||||||
machines = configsPerSystem;
|
machines = configsPerSystem;
|
||||||
machinesFunc = configsFuncPerSystem;
|
machinesFunc = configsFuncPerSystem;
|
||||||
all-machines-json = lib.mapAttrs
|
all-machines-json = lib.mapAttrs (
|
||||||
(system: configs: nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs))
|
system: configs:
|
||||||
configsPerSystem;
|
nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (
|
||||||
|
lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs
|
||||||
|
)
|
||||||
|
) configsPerSystem;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{ lib, clan-core, nixpkgs, ... }:
|
{
|
||||||
|
lib,
|
||||||
|
clan-core,
|
||||||
|
nixpkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
jsonschema = import ./jsonschema { inherit lib; };
|
jsonschema = import ./jsonschema { inherit lib; };
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{ lib
|
{
|
||||||
, inputs
|
lib,
|
||||||
, self
|
inputs,
|
||||||
, ...
|
self,
|
||||||
}: {
|
...
|
||||||
imports = [
|
}:
|
||||||
./jsonschema/flake-module.nix
|
{
|
||||||
];
|
imports = [ ./jsonschema/flake-module.nix ];
|
||||||
flake.lib = import ./default.nix {
|
flake.lib = import ./default.nix {
|
||||||
inherit lib;
|
inherit lib;
|
||||||
inherit (inputs) nixpkgs;
|
inherit (inputs) nixpkgs;
|
||||||
|
@ -1,243 +1,290 @@
|
|||||||
{ lib ? import <nixpkgs/lib>
|
{
|
||||||
, excludedTypes ? [
|
lib ? import <nixpkgs/lib>,
|
||||||
|
excludedTypes ? [
|
||||||
"functionTo"
|
"functionTo"
|
||||||
"package"
|
"package"
|
||||||
]
|
],
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
# remove _module attribute from options
|
# remove _module attribute from options
|
||||||
clean = opts: builtins.removeAttrs opts [ "_module" ];
|
clean = opts: builtins.removeAttrs opts [ "_module" ];
|
||||||
|
|
||||||
# throw error if option type is not supported
|
# throw error if option type is not supported
|
||||||
notSupported = option: lib.trace option throw ''
|
notSupported =
|
||||||
option type '${option.type.name}' ('${option.type.description}') not supported by jsonschema converter
|
option:
|
||||||
location: ${lib.concatStringsSep "." option.loc}
|
lib.trace option throw ''
|
||||||
'';
|
option type '${option.type.name}' ('${option.type.description}') not supported by jsonschema converter
|
||||||
|
location: ${lib.concatStringsSep "." option.loc}
|
||||||
|
'';
|
||||||
|
|
||||||
isExcludedOption = option: (lib.elem (option.type.name or null) excludedTypes);
|
isExcludedOption = option: (lib.elem (option.type.name or null) excludedTypes);
|
||||||
|
|
||||||
filterExcluded = lib.filter (opt: ! isExcludedOption opt);
|
filterExcluded = lib.filter (opt: !isExcludedOption opt);
|
||||||
|
|
||||||
filterExcludedAttrs = lib.filterAttrs (_name: opt: ! isExcludedOption opt);
|
filterExcludedAttrs = lib.filterAttrs (_name: opt: !isExcludedOption opt);
|
||||||
|
|
||||||
allBasicTypes =
|
|
||||||
[ "boolean" "integer" "number" "string" "array" "object" "null" ];
|
|
||||||
|
|
||||||
|
allBasicTypes = [
|
||||||
|
"boolean"
|
||||||
|
"integer"
|
||||||
|
"number"
|
||||||
|
"string"
|
||||||
|
"array"
|
||||||
|
"object"
|
||||||
|
"null"
|
||||||
|
];
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
|
|
||||||
# parses a nixos module to a jsonschema
|
# parses a nixos module to a jsonschema
|
||||||
parseModule = module:
|
parseModule =
|
||||||
|
module:
|
||||||
let
|
let
|
||||||
evaled = lib.evalModules {
|
evaled = lib.evalModules { modules = [ module ]; };
|
||||||
modules = [ module ];
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
parseOptions evaled.options;
|
parseOptions evaled.options;
|
||||||
|
|
||||||
# parses a set of evaluated nixos options to a jsonschema
|
# parses a set of evaluated nixos options to a jsonschema
|
||||||
parseOptions = options':
|
parseOptions =
|
||||||
|
options':
|
||||||
let
|
let
|
||||||
options = filterExcludedAttrs (clean options');
|
options = filterExcludedAttrs (clean options');
|
||||||
# parse options to jsonschema properties
|
# parse options to jsonschema properties
|
||||||
properties = lib.mapAttrs (_name: option: parseOption option) options;
|
properties = lib.mapAttrs (_name: option: parseOption option) options;
|
||||||
# TODO: figure out how to handle if prop.anyOf is used
|
# TODO: figure out how to handle if prop.anyOf is used
|
||||||
isRequired = prop: ! (prop ? default || prop.type or null == "object");
|
isRequired = prop: !(prop ? default || prop.type or null == "object");
|
||||||
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
|
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
|
||||||
required = lib.optionalAttrs (requiredProps != { }) {
|
required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
|
||||||
required = lib.attrNames requiredProps;
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
# return jsonschema
|
# return jsonschema
|
||||||
required // {
|
required
|
||||||
|
// {
|
||||||
type = "object";
|
type = "object";
|
||||||
inherit properties;
|
inherit properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
# parses and evaluated nixos option to a jsonschema property definition
|
# parses and evaluated nixos option to a jsonschema property definition
|
||||||
parseOption = option:
|
parseOption =
|
||||||
|
option:
|
||||||
let
|
let
|
||||||
default = lib.optionalAttrs (option ? default) {
|
default = lib.optionalAttrs (option ? default) { inherit (option) default; };
|
||||||
inherit (option) default;
|
|
||||||
};
|
|
||||||
description = lib.optionalAttrs (option ? description) {
|
description = lib.optionalAttrs (option ? description) {
|
||||||
description = option.description.text or option.description;
|
description = option.description.text or option.description;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
# either type
|
# either type
|
||||||
# TODO: if all nested optiosn are excluded, the parent sould be excluded too
|
# TODO: if all nested optiosn are excluded, the parent sould be excluded too
|
||||||
if option.type.name or null == "either"
|
if
|
||||||
|
option.type.name or null == "either"
|
||||||
# return jsonschema property definition for either
|
# return jsonschema property definition for either
|
||||||
then
|
then
|
||||||
let
|
let
|
||||||
optionsList' = [
|
optionsList' = [
|
||||||
{ type = option.type.nestedTypes.left; _type = "option"; loc = option.loc; }
|
{
|
||||||
{ type = option.type.nestedTypes.right; _type = "option"; loc = option.loc; }
|
type = option.type.nestedTypes.left;
|
||||||
|
_type = "option";
|
||||||
|
loc = option.loc;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
type = option.type.nestedTypes.right;
|
||||||
|
_type = "option";
|
||||||
|
loc = option.loc;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
optionsList = filterExcluded optionsList';
|
optionsList = filterExcluded optionsList';
|
||||||
in
|
in
|
||||||
default // description // {
|
default // description // { anyOf = map parseOption optionsList; }
|
||||||
anyOf = map parseOption optionsList;
|
|
||||||
}
|
|
||||||
|
|
||||||
# handle nested options (not a submodule)
|
# handle nested options (not a submodule)
|
||||||
else if ! option ? _type
|
else if !option ? _type then
|
||||||
then parseOptions option
|
parseOptions option
|
||||||
|
|
||||||
# throw if not an option
|
# throw if not an option
|
||||||
else if option._type != "option" && option._type != "option-type"
|
else if option._type != "option" && option._type != "option-type" then
|
||||||
then throw "parseOption: not an option"
|
throw "parseOption: not an option"
|
||||||
|
|
||||||
# parse nullOr
|
# parse nullOr
|
||||||
else if option.type.name == "nullOr"
|
else if
|
||||||
|
option.type.name == "nullOr"
|
||||||
# return jsonschema property definition for nullOr
|
# return jsonschema property definition for nullOr
|
||||||
then
|
then
|
||||||
let
|
let
|
||||||
nestedOption =
|
nestedOption = {
|
||||||
{ type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; };
|
type = option.type.nestedTypes.elemType;
|
||||||
|
_type = "option";
|
||||||
|
loc = option.loc;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
default // description // {
|
default
|
||||||
anyOf =
|
// description
|
||||||
[{ type = "null"; }]
|
// {
|
||||||
++ (
|
anyOf = [
|
||||||
lib.optional (! isExcludedOption nestedOption)
|
{ type = "null"; }
|
||||||
(parseOption nestedOption)
|
] ++ (lib.optional (!isExcludedOption nestedOption) (parseOption nestedOption));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# parse bool
|
# parse bool
|
||||||
else if option.type.name == "bool"
|
else if
|
||||||
|
option.type.name == "bool"
|
||||||
# return jsonschema property definition for bool
|
# return jsonschema property definition for bool
|
||||||
then default // description // {
|
then
|
||||||
type = "boolean";
|
default // description // { type = "boolean"; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse float
|
# parse float
|
||||||
else if option.type.name == "float"
|
else if
|
||||||
|
option.type.name == "float"
|
||||||
# return jsonschema property definition for float
|
# return jsonschema property definition for float
|
||||||
then default // description // {
|
then
|
||||||
type = "number";
|
default // description // { type = "number"; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse int
|
# parse int
|
||||||
else if (option.type.name == "int" || option.type.name == "positiveInt")
|
else if
|
||||||
|
(option.type.name == "int" || option.type.name == "positiveInt")
|
||||||
# return jsonschema property definition for int
|
# return jsonschema property definition for int
|
||||||
then default // description // {
|
then
|
||||||
type = "integer";
|
default // description // { type = "integer"; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse string
|
# parse string
|
||||||
else if option.type.name == "str"
|
else if
|
||||||
|
option.type.name == "str"
|
||||||
# return jsonschema property definition for string
|
# return jsonschema property definition for string
|
||||||
then default // description // {
|
then
|
||||||
type = "string";
|
default // description // { type = "string"; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse string
|
# parse string
|
||||||
else if option.type.name == "path"
|
else if
|
||||||
|
option.type.name == "path"
|
||||||
# return jsonschema property definition for path
|
# return jsonschema property definition for path
|
||||||
then default // description // {
|
then
|
||||||
type = "string";
|
default // description // { type = "string"; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse anything
|
# parse anything
|
||||||
else if option.type.name == "anything"
|
else if
|
||||||
|
option.type.name == "anything"
|
||||||
# return jsonschema property definition for anything
|
# return jsonschema property definition for anything
|
||||||
then default // description // {
|
then
|
||||||
type = allBasicTypes;
|
default // description // { type = allBasicTypes; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse unspecified
|
# parse unspecified
|
||||||
else if option.type.name == "unspecified"
|
else if
|
||||||
|
option.type.name == "unspecified"
|
||||||
# return jsonschema property definition for unspecified
|
# return jsonschema property definition for unspecified
|
||||||
then default // description // {
|
then
|
||||||
type = allBasicTypes;
|
default // description // { type = allBasicTypes; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse raw
|
# parse raw
|
||||||
else if option.type.name == "raw"
|
else if
|
||||||
|
option.type.name == "raw"
|
||||||
# return jsonschema property definition for raw
|
# return jsonschema property definition for raw
|
||||||
then default // description // {
|
then
|
||||||
type = allBasicTypes;
|
default // description // { type = allBasicTypes; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse enum
|
# parse enum
|
||||||
else if option.type.name == "enum"
|
else if
|
||||||
|
option.type.name == "enum"
|
||||||
# return jsonschema property definition for enum
|
# return jsonschema property definition for enum
|
||||||
then default // description // {
|
then
|
||||||
enum = option.type.functor.payload;
|
default // description // { enum = option.type.functor.payload; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse listOf submodule
|
# parse listOf submodule
|
||||||
else if option.type.name == "listOf" && option.type.functor.wrapped.name == "submodule"
|
else if
|
||||||
|
option.type.name == "listOf" && option.type.functor.wrapped.name == "submodule"
|
||||||
# return jsonschema property definition for listOf submodule
|
# return jsonschema property definition for listOf submodule
|
||||||
then default // description // {
|
then
|
||||||
type = "array";
|
default
|
||||||
items = parseOptions (option.type.functor.wrapped.getSubOptions option.loc);
|
// description
|
||||||
}
|
// {
|
||||||
|
type = "array";
|
||||||
|
items = parseOptions (option.type.functor.wrapped.getSubOptions option.loc);
|
||||||
|
}
|
||||||
|
|
||||||
# parse list
|
# parse list
|
||||||
else if (option.type.name == "listOf")
|
else if
|
||||||
|
(option.type.name == "listOf")
|
||||||
# return jsonschema property definition for list
|
# return jsonschema property definition for list
|
||||||
then
|
then
|
||||||
let
|
let
|
||||||
nestedOption = { type = option.type.functor.wrapped; _type = "option"; loc = option.loc; };
|
nestedOption = {
|
||||||
|
type = option.type.functor.wrapped;
|
||||||
|
_type = "option";
|
||||||
|
loc = option.loc;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
default // description // {
|
default
|
||||||
|
// description
|
||||||
|
// {
|
||||||
type = "array";
|
type = "array";
|
||||||
}
|
}
|
||||||
// (lib.optionalAttrs (! isExcludedOption nestedOption) {
|
// (lib.optionalAttrs (!isExcludedOption nestedOption) { items = parseOption nestedOption; })
|
||||||
items = parseOption nestedOption;
|
|
||||||
})
|
|
||||||
|
|
||||||
# parse list of unspecified
|
# parse list of unspecified
|
||||||
else if
|
else if
|
||||||
(option.type.name == "listOf")
|
(option.type.name == "listOf") && (option.type.functor.wrapped.name == "unspecified")
|
||||||
&& (option.type.functor.wrapped.name == "unspecified")
|
|
||||||
# return jsonschema property definition for list
|
# return jsonschema property definition for list
|
||||||
then default // description // {
|
then
|
||||||
type = "array";
|
default // description // { type = "array"; }
|
||||||
}
|
|
||||||
|
|
||||||
# parse attrsOf submodule
|
# parse attrsOf submodule
|
||||||
else if option.type.name == "attrsOf" && option.type.nestedTypes.elemType.name == "submodule"
|
else if
|
||||||
|
option.type.name == "attrsOf" && option.type.nestedTypes.elemType.name == "submodule"
|
||||||
# return jsonschema property definition for attrsOf submodule
|
# return jsonschema property definition for attrsOf submodule
|
||||||
then default // description // {
|
then
|
||||||
type = "object";
|
default
|
||||||
additionalProperties = parseOptions (option.type.nestedTypes.elemType.getSubOptions option.loc);
|
// description
|
||||||
}
|
// {
|
||||||
|
type = "object";
|
||||||
|
additionalProperties = parseOptions (option.type.nestedTypes.elemType.getSubOptions option.loc);
|
||||||
|
}
|
||||||
|
|
||||||
# parse attrs
|
# parse attrs
|
||||||
else if option.type.name == "attrs"
|
else if
|
||||||
|
option.type.name == "attrs"
|
||||||
# return jsonschema property definition for attrs
|
# return jsonschema property definition for attrs
|
||||||
then default // description // {
|
then
|
||||||
type = "object";
|
default
|
||||||
additionalProperties = true;
|
// description
|
||||||
}
|
// {
|
||||||
|
type = "object";
|
||||||
|
additionalProperties = true;
|
||||||
|
}
|
||||||
|
|
||||||
# parse attrsOf
|
# parse attrsOf
|
||||||
# TODO: if nested option is excluded, the parent sould be excluded too
|
# TODO: if nested option is excluded, the parent sould be excluded too
|
||||||
else if option.type.name == "attrsOf" || option.type.name == "lazyAttrsOf"
|
else if
|
||||||
|
option.type.name == "attrsOf" || option.type.name == "lazyAttrsOf"
|
||||||
# return jsonschema property definition for attrs
|
# return jsonschema property definition for attrs
|
||||||
then
|
then
|
||||||
let
|
let
|
||||||
nestedOption = { type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; };
|
nestedOption = {
|
||||||
|
type = option.type.nestedTypes.elemType;
|
||||||
|
_type = "option";
|
||||||
|
loc = option.loc;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
default // description // {
|
default
|
||||||
|
// description
|
||||||
|
// {
|
||||||
type = "object";
|
type = "object";
|
||||||
additionalProperties =
|
additionalProperties =
|
||||||
if ! isExcludedOption nestedOption
|
if !isExcludedOption nestedOption then
|
||||||
then parseOption { type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; }
|
parseOption {
|
||||||
else false;
|
type = option.type.nestedTypes.elemType;
|
||||||
|
_type = "option";
|
||||||
|
loc = option.loc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
false;
|
||||||
}
|
}
|
||||||
|
|
||||||
# parse submodule
|
# parse submodule
|
||||||
else if option.type.name == "submodule"
|
else if
|
||||||
|
option.type.name == "submodule"
|
||||||
# return jsonschema property definition for submodule
|
# return jsonschema property definition for submodule
|
||||||
# then (lib.attrNames (option.type.getSubOptions option.loc).opt)
|
# then (lib.attrNames (option.type.getSubOptions option.loc).opt)
|
||||||
then parseOptions (option.type.getSubOptions option.loc)
|
then
|
||||||
|
parseOptions (option.type.getSubOptions option.loc)
|
||||||
|
|
||||||
# throw error if option type is not supported
|
# throw error if option type is not supported
|
||||||
else notSupported option;
|
else
|
||||||
|
notSupported option;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/*
|
# An example nixos module declaring an interface.
|
||||||
An example nixos module declaring an interface.
|
{ lib, ... }:
|
||||||
*/
|
{
|
||||||
{ lib, ... }: {
|
|
||||||
options = {
|
options = {
|
||||||
# str
|
# str
|
||||||
name = lib.mkOption {
|
name = lib.mkOption {
|
||||||
@ -44,7 +43,11 @@
|
|||||||
# list of str
|
# list of str
|
||||||
kernelModules = lib.mkOption {
|
kernelModules = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ "nvme" "xhci_pci" "ahci" ];
|
default = [
|
||||||
|
"nvme"
|
||||||
|
"xhci_pci"
|
||||||
|
"ahci"
|
||||||
|
];
|
||||||
description = "A list of enabled kernel modules";
|
description = "A list of enabled kernel modules";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,29 +1,31 @@
|
|||||||
{
|
{
|
||||||
perSystem = { pkgs, ... }: {
|
perSystem =
|
||||||
checks = {
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
checks = {
|
||||||
|
|
||||||
# check if the `clan config` example jsonschema and data is valid
|
# check if the `clan config` example jsonschema and data is valid
|
||||||
lib-jsonschema-example-valid = pkgs.runCommand "lib-jsonschema-example-valid" { } ''
|
lib-jsonschema-example-valid = pkgs.runCommand "lib-jsonschema-example-valid" { } ''
|
||||||
echo "Checking that example-schema.json is valid"
|
echo "Checking that example-schema.json is valid"
|
||||||
${pkgs.check-jsonschema}/bin/check-jsonschema \
|
${pkgs.check-jsonschema}/bin/check-jsonschema \
|
||||||
--check-metaschema ${./.}/example-schema.json
|
--check-metaschema ${./.}/example-schema.json
|
||||||
|
|
||||||
echo "Checking that example-data.json is valid according to example-schema.json"
|
echo "Checking that example-data.json is valid according to example-schema.json"
|
||||||
${pkgs.check-jsonschema}/bin/check-jsonschema \
|
${pkgs.check-jsonschema}/bin/check-jsonschema \
|
||||||
--schemafile ${./.}/example-schema.json \
|
--schemafile ${./.}/example-schema.json \
|
||||||
${./.}/example-data.json
|
${./.}/example-data.json
|
||||||
|
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# check if the `clan config` nix jsonschema converter unit tests succeed
|
# check if the `clan config` nix jsonschema converter unit tests succeed
|
||||||
lib-jsonschema-nix-unit-tests = pkgs.runCommand "lib-jsonschema-nix-unit-tests" { } ''
|
lib-jsonschema-nix-unit-tests = pkgs.runCommand "lib-jsonschema-nix-unit-tests" { } ''
|
||||||
export NIX_PATH=nixpkgs=${pkgs.path}
|
export NIX_PATH=nixpkgs=${pkgs.path}
|
||||||
${pkgs.nix-unit}/bin/nix-unit \
|
${pkgs.nix-unit}/bin/nix-unit \
|
||||||
${./.}/test.nix \
|
${./.}/test.nix \
|
||||||
--eval-store $(realpath .)
|
--eval-store $(realpath .)
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# run these tests via `nix-unit ./test.nix`
|
# run these tests via `nix-unit ./test.nix`
|
||||||
{ lib ? (import <nixpkgs> { }).lib
|
{
|
||||||
, slib ? import ./. { inherit lib; }
|
lib ? (import <nixpkgs> { }).lib,
|
||||||
|
slib ? import ./. { inherit lib; },
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
parseOption = import ./test_parseOption.nix { inherit lib slib; };
|
parseOption = import ./test_parseOption.nix { inherit lib slib; };
|
||||||
|
@ -1,21 +1,25 @@
|
|||||||
# tests for the nixos options to jsonschema converter
|
# tests for the nixos options to jsonschema converter
|
||||||
# run these tests via `nix-unit ./test.nix`
|
# run these tests via `nix-unit ./test.nix`
|
||||||
{ lib ? (import <nixpkgs> { }).lib
|
{
|
||||||
, slib ? import ./. { inherit lib; }
|
lib ? (import <nixpkgs> { }).lib,
|
||||||
|
slib ? import ./. { inherit lib; },
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
description = "Test Description";
|
description = "Test Description";
|
||||||
|
|
||||||
evalType = type: default:
|
evalType =
|
||||||
|
type: default:
|
||||||
let
|
let
|
||||||
evaledConfig = lib.evalModules {
|
evaledConfig = lib.evalModules {
|
||||||
modules = [{
|
modules = [
|
||||||
options.opt = lib.mkOption {
|
{
|
||||||
inherit type;
|
options.opt = lib.mkOption {
|
||||||
inherit default;
|
inherit type;
|
||||||
inherit description;
|
inherit default;
|
||||||
};
|
inherit description;
|
||||||
}];
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
evaledConfig.options.opt;
|
evaledConfig.options.opt;
|
||||||
@ -25,11 +29,7 @@ in
|
|||||||
testNoDefaultNoDescription =
|
testNoDefaultNoDescription =
|
||||||
let
|
let
|
||||||
evaledConfig = lib.evalModules {
|
evaledConfig = lib.evalModules {
|
||||||
modules = [{
|
modules = [ { options.opt = lib.mkOption { type = lib.types.bool; }; } ];
|
||||||
options.opt = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
};
|
|
||||||
}];
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -42,15 +42,17 @@ in
|
|||||||
testDescriptionIsAttrs =
|
testDescriptionIsAttrs =
|
||||||
let
|
let
|
||||||
evaledConfig = lib.evalModules {
|
evaledConfig = lib.evalModules {
|
||||||
modules = [{
|
modules = [
|
||||||
options.opt = lib.mkOption {
|
{
|
||||||
type = lib.types.bool;
|
options.opt = lib.mkOption {
|
||||||
description = {
|
type = lib.types.bool;
|
||||||
_type = "mdDoc";
|
description = {
|
||||||
text = description;
|
_type = "mdDoc";
|
||||||
|
text = description;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}];
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -112,7 +114,11 @@ in
|
|||||||
testEnum =
|
testEnum =
|
||||||
let
|
let
|
||||||
default = "foo";
|
default = "foo";
|
||||||
values = [ "foo" "bar" "baz" ];
|
values = [
|
||||||
|
"foo"
|
||||||
|
"bar"
|
||||||
|
"baz"
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.enum values) default);
|
expr = slib.parseOption (evalType (lib.types.enum values) default);
|
||||||
@ -124,7 +130,11 @@ in
|
|||||||
|
|
||||||
testListOfInt =
|
testListOfInt =
|
||||||
let
|
let
|
||||||
default = [ 1 2 3 ];
|
default = [
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.listOf lib.types.int) default);
|
expr = slib.parseOption (evalType (lib.types.listOf lib.types.int) default);
|
||||||
@ -139,14 +149,26 @@ in
|
|||||||
|
|
||||||
testListOfUnspecified =
|
testListOfUnspecified =
|
||||||
let
|
let
|
||||||
default = [ 1 2 3 ];
|
default = [
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.listOf lib.types.unspecified) default);
|
expr = slib.parseOption (evalType (lib.types.listOf lib.types.unspecified) default);
|
||||||
expected = {
|
expected = {
|
||||||
type = "array";
|
type = "array";
|
||||||
items = {
|
items = {
|
||||||
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
|
type = [
|
||||||
|
"boolean"
|
||||||
|
"integer"
|
||||||
|
"number"
|
||||||
|
"string"
|
||||||
|
"array"
|
||||||
|
"object"
|
||||||
|
"null"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
inherit default description;
|
inherit default description;
|
||||||
};
|
};
|
||||||
@ -154,7 +176,11 @@ in
|
|||||||
|
|
||||||
testAttrs =
|
testAttrs =
|
||||||
let
|
let
|
||||||
default = { foo = 1; bar = 2; baz = 3; };
|
default = {
|
||||||
|
foo = 1;
|
||||||
|
bar = 2;
|
||||||
|
baz = 3;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.attrs) default);
|
expr = slib.parseOption (evalType (lib.types.attrs) default);
|
||||||
@ -167,7 +193,11 @@ in
|
|||||||
|
|
||||||
testAttrsOfInt =
|
testAttrsOfInt =
|
||||||
let
|
let
|
||||||
default = { foo = 1; bar = 2; baz = 3; };
|
default = {
|
||||||
|
foo = 1;
|
||||||
|
bar = 2;
|
||||||
|
baz = 3;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.attrsOf lib.types.int) default);
|
expr = slib.parseOption (evalType (lib.types.attrsOf lib.types.int) default);
|
||||||
@ -182,7 +212,11 @@ in
|
|||||||
|
|
||||||
testLazyAttrsOfInt =
|
testLazyAttrsOfInt =
|
||||||
let
|
let
|
||||||
default = { foo = 1; bar = 2; baz = 3; };
|
default = {
|
||||||
|
foo = 1;
|
||||||
|
bar = 2;
|
||||||
|
baz = 3;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.lazyAttrsOf lib.types.int) default);
|
expr = slib.parseOption (evalType (lib.types.lazyAttrsOf lib.types.int) default);
|
||||||
@ -286,7 +320,10 @@ in
|
|||||||
inherit description;
|
inherit description;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
default = { foo.opt = false; bar.opt = true; };
|
default = {
|
||||||
|
foo.opt = false;
|
||||||
|
bar.opt = true;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default);
|
expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default);
|
||||||
@ -315,7 +352,10 @@ in
|
|||||||
inherit description;
|
inherit description;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
default = [{ opt = false; } { opt = true; }];
|
default = [
|
||||||
|
{ opt = false; }
|
||||||
|
{ opt = true; }
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default);
|
expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default);
|
||||||
@ -358,7 +398,15 @@ in
|
|||||||
expr = slib.parseOption (evalType lib.types.anything default);
|
expr = slib.parseOption (evalType lib.types.anything default);
|
||||||
expected = {
|
expected = {
|
||||||
inherit default description;
|
inherit default description;
|
||||||
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
|
type = [
|
||||||
|
"boolean"
|
||||||
|
"integer"
|
||||||
|
"number"
|
||||||
|
"string"
|
||||||
|
"array"
|
||||||
|
"object"
|
||||||
|
"null"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -370,7 +418,15 @@ in
|
|||||||
expr = slib.parseOption (evalType lib.types.unspecified default);
|
expr = slib.parseOption (evalType lib.types.unspecified default);
|
||||||
expected = {
|
expected = {
|
||||||
inherit default description;
|
inherit default description;
|
||||||
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
|
type = [
|
||||||
|
"boolean"
|
||||||
|
"integer"
|
||||||
|
"number"
|
||||||
|
"string"
|
||||||
|
"array"
|
||||||
|
"object"
|
||||||
|
"null"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -382,7 +438,15 @@ in
|
|||||||
expr = slib.parseOption (evalType lib.types.raw default);
|
expr = slib.parseOption (evalType lib.types.raw default);
|
||||||
expected = {
|
expected = {
|
||||||
inherit default description;
|
inherit default description;
|
||||||
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
|
type = [
|
||||||
|
"boolean"
|
||||||
|
"integer"
|
||||||
|
"number"
|
||||||
|
"string"
|
||||||
|
"array"
|
||||||
|
"object"
|
||||||
|
"null"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
# tests for the nixos options to jsonschema converter
|
# tests for the nixos options to jsonschema converter
|
||||||
# run these tests via `nix-unit ./test.nix`
|
# run these tests via `nix-unit ./test.nix`
|
||||||
{ lib ? (import <nixpkgs> { }).lib
|
{
|
||||||
, slib ? import ./. { inherit lib; }
|
lib ? (import <nixpkgs> { }).lib,
|
||||||
|
slib ? import ./. { inherit lib; },
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
evaledOptions =
|
evaledOptions =
|
||||||
let
|
let
|
||||||
evaledConfig = lib.evalModules {
|
evaledConfig = lib.evalModules { modules = [ ./example-interface.nix ]; };
|
||||||
modules = [ ./example-interface.nix ];
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
evaledConfig.options;
|
evaledConfig.options;
|
||||||
in
|
in
|
||||||
@ -21,11 +20,7 @@ in
|
|||||||
testParseNestedOptions =
|
testParseNestedOptions =
|
||||||
let
|
let
|
||||||
evaled = lib.evalModules {
|
evaled = lib.evalModules {
|
||||||
modules = [{
|
modules = [ { options.foo.bar = lib.mkOption { type = lib.types.bool; }; } ];
|
||||||
options.foo.bar = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
};
|
|
||||||
}];
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -34,7 +29,9 @@ in
|
|||||||
properties = {
|
properties = {
|
||||||
foo = {
|
foo = {
|
||||||
properties = {
|
properties = {
|
||||||
bar = { type = "boolean"; };
|
bar = {
|
||||||
|
type = "boolean";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
required = [ "bar" ];
|
required = [ "bar" ];
|
||||||
type = "object";
|
type = "object";
|
||||||
|
@ -1,45 +1,48 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [ ./state.nix ];
|
||||||
./state.nix
|
|
||||||
];
|
|
||||||
options.clanCore.backups = {
|
options.clanCore.backups = {
|
||||||
providers = lib.mkOption {
|
providers = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
|
type = lib.types.attrsOf (
|
||||||
options = {
|
lib.types.submodule (
|
||||||
name = lib.mkOption {
|
{ name, ... }:
|
||||||
type = lib.types.str;
|
{
|
||||||
default = name;
|
options = {
|
||||||
description = ''
|
name = lib.mkOption {
|
||||||
Name of the backup provider
|
type = lib.types.str;
|
||||||
'';
|
default = name;
|
||||||
};
|
description = ''
|
||||||
list = lib.mkOption {
|
Name of the backup provider
|
||||||
type = lib.types.str;
|
'';
|
||||||
description = ''
|
};
|
||||||
script to list backups
|
list = lib.mkOption {
|
||||||
'';
|
type = lib.types.str;
|
||||||
};
|
description = ''
|
||||||
restore = lib.mkOption {
|
script to list backups
|
||||||
type = lib.types.str;
|
'';
|
||||||
description = ''
|
};
|
||||||
script to restore a backup
|
restore = lib.mkOption {
|
||||||
should take an optional service name as argument
|
type = lib.types.str;
|
||||||
gets ARCHIVE_ID, LOCATION, JOB and FOLDERS as environment variables
|
description = ''
|
||||||
ARCHIVE_ID is the id of the backup
|
script to restore a backup
|
||||||
LOCATION is the remote identifier of the backup
|
should take an optional service name as argument
|
||||||
JOB is the job name of the backup
|
gets ARCHIVE_ID, LOCATION, JOB and FOLDERS as environment variables
|
||||||
FOLDERS is a colon separated list of folders to restore
|
ARCHIVE_ID is the id of the backup
|
||||||
'';
|
LOCATION is the remote identifier of the backup
|
||||||
};
|
JOB is the job name of the backup
|
||||||
create = lib.mkOption {
|
FOLDERS is a colon separated list of folders to restore
|
||||||
type = lib.types.str;
|
'';
|
||||||
description = ''
|
};
|
||||||
script to start a backup
|
create = lib.mkOption {
|
||||||
'';
|
type = lib.types.str;
|
||||||
};
|
description = ''
|
||||||
};
|
script to start a backup
|
||||||
}));
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
default = { };
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Configured backup providers which are used by this machine
|
Configured backup providers which are used by this machine
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
{ lib
|
{ lib, ... }:
|
||||||
, ...
|
{
|
||||||
}: {
|
|
||||||
/*
|
/*
|
||||||
Declaring imports inside the module system does not trigger an infinite
|
Declaring imports inside the module system does not trigger an infinite
|
||||||
recursion in this case because buildClan generates the imports from the
|
recursion in this case because buildClan generates the imports from the
|
||||||
|
@ -1 +1,4 @@
|
|||||||
{ pkgs, ... }: { documentation.nixos.enable = pkgs.lib.mkDefault false; }
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
documentation.nixos.enable = pkgs.lib.mkDefault false;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ lib, pkgs, ... }: {
|
{ lib, pkgs, ... }:
|
||||||
|
{
|
||||||
options.clanCore = {
|
options.clanCore = {
|
||||||
clanName = lib.mkOption {
|
clanName = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
@ -49,7 +49,18 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRenamedOptionModule [ "clan" "networking" "deploymentAddress" ] [ "clan" "networking" "targetHost" ])
|
(lib.mkRenamedOptionModule
|
||||||
|
[
|
||||||
|
"clan"
|
||||||
|
"networking"
|
||||||
|
"deploymentAddress"
|
||||||
|
]
|
||||||
|
[
|
||||||
|
"clan"
|
||||||
|
"networking"
|
||||||
|
"targetHost"
|
||||||
|
]
|
||||||
|
)
|
||||||
];
|
];
|
||||||
config = {
|
config = {
|
||||||
# conflicts with systemd-resolved
|
# conflicts with systemd-resolved
|
||||||
@ -64,16 +75,18 @@
|
|||||||
systemd.network.wait-online.enable = false;
|
systemd.network.wait-online.enable = false;
|
||||||
|
|
||||||
# Provide a default network configuration but don't compete with network-manager or dhcpcd
|
# Provide a default network configuration but don't compete with network-manager or dhcpcd
|
||||||
systemd.network.networks."50-uplink" = lib.mkIf (!(config.networking.networkmanager.enable || config.networking.dhcpcd.enable)) {
|
systemd.network.networks."50-uplink" =
|
||||||
matchConfig.Type = "ether";
|
lib.mkIf (!(config.networking.networkmanager.enable || config.networking.dhcpcd.enable))
|
||||||
networkConfig = {
|
{
|
||||||
DHCP = "yes";
|
matchConfig.Type = "ether";
|
||||||
LLDP = "yes";
|
networkConfig = {
|
||||||
LLMNR = "yes";
|
DHCP = "yes";
|
||||||
MulticastDNS = "yes";
|
LLDP = "yes";
|
||||||
IPv6AcceptRA = "yes";
|
LLMNR = "yes";
|
||||||
};
|
MulticastDNS = "yes";
|
||||||
};
|
IPv6AcceptRA = "yes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Use networkd instead of the pile of shell scripts
|
# Use networkd instead of the pile of shell scripts
|
||||||
networking.useNetworkd = lib.mkDefault true;
|
networking.useNetworkd = lib.mkDefault true;
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
{ pkgs, options, lib, ... }: {
|
{
|
||||||
|
pkgs,
|
||||||
|
options,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
options.clanCore.optionsNix = lib.mkOption {
|
options.clanCore.optionsNix = lib.mkOption {
|
||||||
type = lib.types.raw;
|
type = lib.types.raw;
|
||||||
internal = true;
|
internal = true;
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
{ config, lib, pkgs, ... }: {
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
# TODO: factor these out into a separate interface.nix.
|
# TODO: factor these out into a separate interface.nix.
|
||||||
# Also think about moving these options out of `system.clan`.
|
# Also think about moving these options out of `system.clan`.
|
||||||
# Maybe we should not re-use the already polluted confg.system namespace
|
# Maybe we should not re-use the already polluted confg.system namespace
|
||||||
@ -90,6 +96,8 @@
|
|||||||
inherit (config.clan.deployment) requireExplicitUpdate;
|
inherit (config.clan.deployment) requireExplicitUpdate;
|
||||||
inherit (config.clanCore) secretsUploadDirectory;
|
inherit (config.clanCore) secretsUploadDirectory;
|
||||||
};
|
};
|
||||||
system.clan.deployment.file = pkgs.writeText "deployment.json" (builtins.toJSON config.system.clan.deployment.data);
|
system.clan.deployment.file = pkgs.writeText "deployment.json" (
|
||||||
|
builtins.toJSON config.system.clan.deployment.data
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ pkgs, ... }: {
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
# essential debugging tools for networked services
|
# essential debugging tools for networked services
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
pkgs.dnsutils
|
pkgs.dnsutils
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
options.clanCore.secretStore = lib.mkOption {
|
options.clanCore.secretStore = lib.mkOption {
|
||||||
type = lib.types.enum [ "sops" "password-store" "vm" "custom" ];
|
type = lib.types.enum [
|
||||||
|
"sops"
|
||||||
|
"password-store"
|
||||||
|
"vm"
|
||||||
|
"custom"
|
||||||
|
];
|
||||||
default = "sops";
|
default = "sops";
|
||||||
description = ''
|
description = ''
|
||||||
method to store secrets
|
method to store secrets
|
||||||
@ -34,8 +44,8 @@
|
|||||||
|
|
||||||
options.clanCore.secrets = lib.mkOption {
|
options.clanCore.secrets = lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf
|
type = lib.types.attrsOf (
|
||||||
(lib.types.submodule (service: {
|
lib.types.submodule (service: {
|
||||||
options = {
|
options = {
|
||||||
name = lib.mkOption {
|
name = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
@ -45,55 +55,60 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
generator = lib.mkOption {
|
generator = lib.mkOption {
|
||||||
type = lib.types.submodule ({ config, ... }: {
|
type = lib.types.submodule (
|
||||||
options = {
|
{ config, ... }:
|
||||||
path = lib.mkOption {
|
{
|
||||||
type = lib.types.listOf (lib.types.either lib.types.path lib.types.package);
|
options = {
|
||||||
default = [ ];
|
path = lib.mkOption {
|
||||||
description = ''
|
type = lib.types.listOf (lib.types.either lib.types.path lib.types.package);
|
||||||
Extra paths to add to the PATH environment variable when running the generator.
|
default = [ ];
|
||||||
'';
|
description = ''
|
||||||
};
|
Extra paths to add to the PATH environment variable when running the generator.
|
||||||
prompt = lib.mkOption {
|
'';
|
||||||
type = lib.types.nullOr lib.types.str;
|
};
|
||||||
default = null;
|
prompt = lib.mkOption {
|
||||||
description = ''
|
type = lib.types.nullOr lib.types.str;
|
||||||
prompt text to ask for a value.
|
default = null;
|
||||||
This value will be passed to the script as the environment variable $prompt_value.
|
description = ''
|
||||||
'';
|
prompt text to ask for a value.
|
||||||
};
|
This value will be passed to the script as the environment variable $prompt_value.
|
||||||
script = lib.mkOption {
|
'';
|
||||||
type = lib.types.str;
|
};
|
||||||
description = ''
|
script = lib.mkOption {
|
||||||
Script to generate the secret.
|
type = lib.types.str;
|
||||||
The script will be called with the following variables:
|
description = ''
|
||||||
- facts: path to a directory where facts can be stored
|
Script to generate the secret.
|
||||||
- secrets: path to a directory where secrets can be stored
|
The script will be called with the following variables:
|
||||||
The script is expected to generate all secrets and facts defined in the module.
|
- facts: path to a directory where facts can be stored
|
||||||
'';
|
- secrets: path to a directory where secrets can be stored
|
||||||
};
|
The script is expected to generate all secrets and facts defined in the module.
|
||||||
finalScript = lib.mkOption {
|
'';
|
||||||
type = lib.types.str;
|
};
|
||||||
readOnly = true;
|
finalScript = lib.mkOption {
|
||||||
internal = true;
|
type = lib.types.str;
|
||||||
default = ''
|
readOnly = true;
|
||||||
set -eu -o pipefail
|
internal = true;
|
||||||
|
default = ''
|
||||||
|
set -eu -o pipefail
|
||||||
|
|
||||||
export PATH="${lib.makeBinPath config.path}:${pkgs.coreutils}/bin"
|
export PATH="${lib.makeBinPath config.path}:${pkgs.coreutils}/bin"
|
||||||
|
|
||||||
# prepare sandbox user
|
# prepare sandbox user
|
||||||
mkdir -p /etc
|
mkdir -p /etc
|
||||||
cp ${pkgs.runCommand "fake-etc" {} ''
|
cp ${
|
||||||
export PATH="${pkgs.coreutils}/bin"
|
pkgs.runCommand "fake-etc" { } ''
|
||||||
mkdir -p $out
|
export PATH="${pkgs.coreutils}/bin"
|
||||||
cp /etc/* $out/
|
mkdir -p $out
|
||||||
''}/* /etc/
|
cp /etc/* $out/
|
||||||
|
''
|
||||||
|
}/* /etc/
|
||||||
|
|
||||||
${config.script}
|
${config.script}
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
secrets =
|
secrets =
|
||||||
let
|
let
|
||||||
@ -101,68 +116,77 @@
|
|||||||
in
|
in
|
||||||
lib.mkOption {
|
lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: {
|
type = lib.types.attrsOf (
|
||||||
options = {
|
lib.types.submodule (
|
||||||
name = lib.mkOption {
|
{ config, name, ... }:
|
||||||
type = lib.types.str;
|
{
|
||||||
description = ''
|
options =
|
||||||
name of the secret
|
{
|
||||||
'';
|
name = lib.mkOption {
|
||||||
default = name;
|
type = lib.types.str;
|
||||||
};
|
description = ''
|
||||||
path = lib.mkOption {
|
name of the secret
|
||||||
type = lib.types.str;
|
'';
|
||||||
description = ''
|
default = name;
|
||||||
path to a secret which is generated by the generator
|
};
|
||||||
'';
|
path = lib.mkOption {
|
||||||
default = "${config'.clanCore.secretsDirectory}/${config'.clanCore.secretsPrefix}${config.name}";
|
type = lib.types.str;
|
||||||
};
|
description = ''
|
||||||
} // lib.optionalAttrs (config'.clanCore.secretStore == "sops") {
|
path to a secret which is generated by the generator
|
||||||
groups = lib.mkOption {
|
'';
|
||||||
type = lib.types.listOf lib.types.str;
|
default = "${config'.clanCore.secretsDirectory}/${config'.clanCore.secretsPrefix}${config.name}";
|
||||||
default = config'.clanCore.sops.defaultGroups;
|
};
|
||||||
description = ''
|
}
|
||||||
Groups to decrypt the secret for. By default we always use the user's key.
|
// lib.optionalAttrs (config'.clanCore.secretStore == "sops") {
|
||||||
'';
|
groups = lib.mkOption {
|
||||||
};
|
type = lib.types.listOf lib.types.str;
|
||||||
};
|
default = config'.clanCore.sops.defaultGroups;
|
||||||
}));
|
description = ''
|
||||||
|
Groups to decrypt the secret for. By default we always use the user's key.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
description = ''
|
description = ''
|
||||||
path where the secret is located in the filesystem
|
path where the secret is located in the filesystem
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
facts = lib.mkOption {
|
facts = lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (lib.types.submodule (fact: {
|
type = lib.types.attrsOf (
|
||||||
options = {
|
lib.types.submodule (fact: {
|
||||||
name = lib.mkOption {
|
options = {
|
||||||
type = lib.types.str;
|
name = lib.mkOption {
|
||||||
description = ''
|
type = lib.types.str;
|
||||||
name of the fact
|
description = ''
|
||||||
'';
|
name of the fact
|
||||||
default = fact.config._module.args.name;
|
'';
|
||||||
|
default = fact.config._module.args.name;
|
||||||
|
};
|
||||||
|
path = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
path to a fact which is generated by the generator
|
||||||
|
'';
|
||||||
|
default =
|
||||||
|
config.clanCore.clanDir
|
||||||
|
+ "/machines/${config.clanCore.machineName}/facts/${fact.config._module.args.name}";
|
||||||
|
};
|
||||||
|
value = lib.mkOption {
|
||||||
|
defaultText = lib.literalExpression "\${config.clanCore.clanDir}/\${fact.config.path}";
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default =
|
||||||
|
if builtins.pathExists fact.config.path then lib.strings.fileContents fact.config.path else null;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
path = lib.mkOption {
|
})
|
||||||
type = lib.types.path;
|
);
|
||||||
description = ''
|
|
||||||
path to a fact which is generated by the generator
|
|
||||||
'';
|
|
||||||
default = config.clanCore.clanDir + "/machines/${config.clanCore.machineName}/facts/${fact.config._module.args.name}";
|
|
||||||
};
|
|
||||||
value = lib.mkOption {
|
|
||||||
defaultText = lib.literalExpression "\${config.clanCore.clanDir}/\${fact.config.path}";
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default =
|
|
||||||
if builtins.pathExists fact.config.path then
|
|
||||||
lib.strings.fileContents fact.config.path
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
imports = [
|
imports = [
|
||||||
./sops.nix
|
./sops.nix
|
||||||
|
@ -13,4 +13,3 @@
|
|||||||
system.clan.secretsModule = "clan_cli.secrets.modules.password_store";
|
system.clan.secretsModule = "clan_cli.secrets.modules.password_store";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,22 +1,33 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
secretsDir = config.clanCore.clanDir + "/sops/secrets";
|
secretsDir = config.clanCore.clanDir + "/sops/secrets";
|
||||||
groupsDir = config.clanCore.clanDir + "/sops/groups";
|
groupsDir = config.clanCore.clanDir + "/sops/groups";
|
||||||
|
|
||||||
|
|
||||||
# My symlink is in the nixos module detected as a directory also it works in the repl. Is this because of pure evaluation?
|
# My symlink is in the nixos module detected as a directory also it works in the repl. Is this because of pure evaluation?
|
||||||
containsSymlink = path:
|
containsSymlink =
|
||||||
builtins.pathExists path && (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink");
|
path:
|
||||||
|
builtins.pathExists path
|
||||||
|
&& (builtins.readFileType path == "directory" || builtins.readFileType path == "symlink");
|
||||||
|
|
||||||
containsMachine = parent: name: type:
|
containsMachine =
|
||||||
|
parent: name: type:
|
||||||
type == "directory" && containsSymlink "${parent}/${name}/machines/${config.clanCore.machineName}";
|
type == "directory" && containsSymlink "${parent}/${name}/machines/${config.clanCore.machineName}";
|
||||||
|
|
||||||
containsMachineOrGroups = name: type:
|
containsMachineOrGroups =
|
||||||
(containsMachine secretsDir name type) || lib.any (group: type == "directory" && containsSymlink "${secretsDir}/${name}/groups/${group}") groups;
|
name: type:
|
||||||
|
(containsMachine secretsDir name type)
|
||||||
|
|| lib.any (
|
||||||
|
group: type == "directory" && containsSymlink "${secretsDir}/${name}/groups/${group}"
|
||||||
|
) groups;
|
||||||
|
|
||||||
filterDir = filter: dir:
|
filterDir =
|
||||||
lib.optionalAttrs (builtins.pathExists dir)
|
filter: dir:
|
||||||
(lib.filterAttrs filter (builtins.readDir dir));
|
lib.optionalAttrs (builtins.pathExists dir) (lib.filterAttrs filter (builtins.readDir dir));
|
||||||
|
|
||||||
groups = builtins.attrNames (filterDir (containsMachine groupsDir) groupsDir);
|
groups = builtins.attrNames (filterDir (containsMachine groupsDir) groupsDir);
|
||||||
secrets = filterDir containsMachineOrGroups secretsDir;
|
secrets = filterDir containsMachineOrGroups secretsDir;
|
||||||
@ -34,17 +45,18 @@ in
|
|||||||
clanCore.secretsDirectory = "/run/secrets";
|
clanCore.secretsDirectory = "/run/secrets";
|
||||||
clanCore.secretsPrefix = config.clanCore.machineName + "-";
|
clanCore.secretsPrefix = config.clanCore.machineName + "-";
|
||||||
system.clan.secretsModule = "clan_cli.secrets.modules.sops";
|
system.clan.secretsModule = "clan_cli.secrets.modules.sops";
|
||||||
sops.secrets = builtins.mapAttrs
|
sops.secrets = builtins.mapAttrs (name: _: {
|
||||||
(name: _: {
|
sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret";
|
||||||
sopsFile = config.clanCore.clanDir + "/sops/secrets/${name}/secret";
|
format = "binary";
|
||||||
format = "binary";
|
}) secrets;
|
||||||
})
|
|
||||||
secrets;
|
|
||||||
# To get proper error messages about missing secrets we need a dummy secret file that is always present
|
# To get proper error messages about missing secrets we need a dummy secret file that is always present
|
||||||
sops.defaultSopsFile = lib.mkIf config.sops.validateSopsFiles (lib.mkDefault (builtins.toString (pkgs.writeText "dummy.yaml" "")));
|
sops.defaultSopsFile = lib.mkIf config.sops.validateSopsFiles (
|
||||||
|
lib.mkDefault (builtins.toString (pkgs.writeText "dummy.yaml" ""))
|
||||||
|
);
|
||||||
|
|
||||||
sops.age.keyFile = lib.mkIf (builtins.pathExists (config.clanCore.clanDir + "/sops/secrets/${config.clanCore.machineName}-age.key/secret"))
|
sops.age.keyFile = lib.mkIf (builtins.pathExists (
|
||||||
(lib.mkDefault "/var/lib/sops-nix/key.txt");
|
config.clanCore.clanDir + "/sops/secrets/${config.clanCore.machineName}-age.key/secret"
|
||||||
|
)) (lib.mkDefault "/var/lib/sops-nix/key.txt");
|
||||||
clanCore.secretsUploadDirectory = lib.mkDefault "/var/lib/sops-nix";
|
clanCore.secretsUploadDirectory = lib.mkDefault "/var/lib/sops-nix";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,4 +7,3 @@
|
|||||||
system.clan.factsModule = "clan_cli.facts.modules.vm";
|
system.clan.factsModule = "clan_cli.facts.modules.vm";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,41 +1,43 @@
|
|||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
{
|
{
|
||||||
# defaults
|
# defaults
|
||||||
config.clanCore.state.HOME.folders = [
|
config.clanCore.state.HOME.folders = [ "/home" ];
|
||||||
"/home"
|
|
||||||
];
|
|
||||||
|
|
||||||
# interface
|
# interface
|
||||||
options.clanCore.state = lib.mkOption {
|
options.clanCore.state = lib.mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf
|
type = lib.types.attrsOf (
|
||||||
(lib.types.submodule ({ ... }: {
|
lib.types.submodule (
|
||||||
options = {
|
{ ... }:
|
||||||
folders = lib.mkOption {
|
{
|
||||||
type = lib.types.listOf lib.types.str;
|
options = {
|
||||||
description = ''
|
folders = lib.mkOption {
|
||||||
Folder where state resides in
|
type = lib.types.listOf lib.types.str;
|
||||||
'';
|
description = ''
|
||||||
};
|
Folder where state resides in
|
||||||
preRestoreScript = lib.mkOption {
|
'';
|
||||||
type = lib.types.str;
|
};
|
||||||
default = ":";
|
preRestoreScript = lib.mkOption {
|
||||||
description = ''
|
type = lib.types.str;
|
||||||
script to run before restoring the state dir from a backup
|
default = ":";
|
||||||
|
description = ''
|
||||||
|
script to run before restoring the state dir from a backup
|
||||||
|
|
||||||
Utilize this to stop services which currently access these folders
|
Utilize this to stop services which currently access these folders
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
postRestoreScript = lib.mkOption {
|
postRestoreScript = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = ":";
|
default = ":";
|
||||||
description = ''
|
description = ''
|
||||||
script to restore the service after the state dir was restored from a backup
|
script to restore the service after the state dir was restored from a backup
|
||||||
|
|
||||||
Utilize this to start services which were previously stopped
|
Utilize this to start services which were previously stopped
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}));
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
{ lib, config, pkgs, options, extendModules, modulesPath, ... }:
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
options,
|
||||||
|
extendModules,
|
||||||
|
modulesPath,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
# Flatten the list of state folders into a single list
|
# Flatten the list of state folders into a single list
|
||||||
stateFolders = lib.flatten (
|
stateFolders = lib.flatten (lib.mapAttrsToList (_item: attrs: attrs.folders) config.clanCore.state);
|
||||||
lib.mapAttrsToList
|
|
||||||
(_item: attrs: attrs.folders)
|
|
||||||
config.clanCore.state
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
vmModule = {
|
vmModule = {
|
||||||
imports = [
|
imports = [
|
||||||
@ -32,7 +35,10 @@ let
|
|||||||
# currently needed for system.etc.overlay.enable
|
# currently needed for system.etc.overlay.enable
|
||||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||||
|
|
||||||
boot.initrd.systemd.storePaths = [ pkgs.util-linux pkgs.e2fsprogs ];
|
boot.initrd.systemd.storePaths = [
|
||||||
|
pkgs.util-linux
|
||||||
|
pkgs.e2fsprogs
|
||||||
|
];
|
||||||
boot.initrd.systemd.emergencyAccess = true;
|
boot.initrd.systemd.emergencyAccess = true;
|
||||||
|
|
||||||
# sysusers is faster than nixos's perl scripts
|
# sysusers is faster than nixos's perl scripts
|
||||||
@ -43,50 +49,72 @@ let
|
|||||||
|
|
||||||
boot.initrd.kernelModules = [ "virtiofs" ];
|
boot.initrd.kernelModules = [ "virtiofs" ];
|
||||||
virtualisation.writableStore = false;
|
virtualisation.writableStore = false;
|
||||||
virtualisation.fileSystems = lib.mkForce ({
|
virtualisation.fileSystems = lib.mkForce (
|
||||||
"/nix/store" = {
|
{
|
||||||
device = "nix-store";
|
"/nix/store" = {
|
||||||
options = [ "x-systemd.requires=systemd-modules-load.service" "ro" ];
|
device = "nix-store";
|
||||||
fsType = "virtiofs";
|
options = [
|
||||||
};
|
"x-systemd.requires=systemd-modules-load.service"
|
||||||
|
"ro"
|
||||||
|
];
|
||||||
|
fsType = "virtiofs";
|
||||||
|
};
|
||||||
|
|
||||||
"/" = {
|
"/" = {
|
||||||
device = "/dev/vda";
|
device = "/dev/vda";
|
||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
options = [ "defaults" "x-systemd.makefs" "nobarrier" "noatime" "nodiratime" "data=writeback" "discard" ];
|
options = [
|
||||||
};
|
"defaults"
|
||||||
|
"x-systemd.makefs"
|
||||||
|
"nobarrier"
|
||||||
|
"noatime"
|
||||||
|
"nodiratime"
|
||||||
|
"data=writeback"
|
||||||
|
"discard"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
"/vmstate" = {
|
"/vmstate" = {
|
||||||
device = "/dev/vdb";
|
device = "/dev/vdb";
|
||||||
options = [ "x-systemd.makefs" "noatime" "nodiratime" "discard" ];
|
options = [
|
||||||
noCheck = true;
|
"x-systemd.makefs"
|
||||||
fsType = "ext4";
|
"noatime"
|
||||||
};
|
"nodiratime"
|
||||||
|
"discard"
|
||||||
|
];
|
||||||
|
noCheck = true;
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
${config.clanCore.secretsUploadDirectory} = {
|
${config.clanCore.secretsUploadDirectory} = {
|
||||||
device = "secrets";
|
device = "secrets";
|
||||||
fsType = "9p";
|
fsType = "9p";
|
||||||
neededForBoot = true;
|
neededForBoot = true;
|
||||||
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
|
options = [
|
||||||
};
|
"trans=virtio"
|
||||||
|
"version=9p2000.L"
|
||||||
} // lib.listToAttrs (map
|
"cache=loose"
|
||||||
(folder:
|
];
|
||||||
lib.nameValuePair folder {
|
};
|
||||||
device = "/vmstate${folder}";
|
}
|
||||||
fsType = "none";
|
// lib.listToAttrs (
|
||||||
options = [ "bind" ];
|
map (
|
||||||
})
|
folder:
|
||||||
stateFolders));
|
lib.nameValuePair folder {
|
||||||
|
device = "/vmstate${folder}";
|
||||||
|
fsType = "none";
|
||||||
|
options = [ "bind" ];
|
||||||
|
}
|
||||||
|
) stateFolders
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
# We cannot simply merge the VM config into the current system config, because
|
# We cannot simply merge the VM config into the current system config, because
|
||||||
# it is not necessarily a VM.
|
# it is not necessarily a VM.
|
||||||
# Instead we use extendModules to create a second instance of the current
|
# Instead we use extendModules to create a second instance of the current
|
||||||
# system configuration, and then merge the VM config into that.
|
# system configuration, and then merge the VM config into that.
|
||||||
vmConfig = extendModules {
|
vmConfig = extendModules { modules = [ vmModule ]; };
|
||||||
modules = [ vmModule ];
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
@ -210,12 +238,14 @@ in
|
|||||||
};
|
};
|
||||||
# for clan vm create
|
# for clan vm create
|
||||||
system.clan.vm = {
|
system.clan.vm = {
|
||||||
create = pkgs.writeText "vm.json" (builtins.toJSON {
|
create = pkgs.writeText "vm.json" (
|
||||||
initrd = "${vmConfig.config.system.build.initialRamdisk}/${vmConfig.config.system.boot.loader.initrdFile}";
|
builtins.toJSON {
|
||||||
toplevel = vmConfig.config.system.build.toplevel;
|
initrd = "${vmConfig.config.system.build.initialRamdisk}/${vmConfig.config.system.boot.loader.initrdFile}";
|
||||||
regInfo = (pkgs.closureInfo { rootPaths = vmConfig.config.virtualisation.additionalPaths; });
|
toplevel = vmConfig.config.system.build.toplevel;
|
||||||
inherit (config.clan.virtualisation) memorySize cores graphics;
|
regInfo = (pkgs.closureInfo { rootPaths = vmConfig.config.virtualisation.additionalPaths; });
|
||||||
});
|
inherit (config.clan.virtualisation) memorySize cores graphics;
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
virtualisation = lib.optionalAttrs (options.virtualisation ? cores) {
|
virtualisation = lib.optionalAttrs (options.virtualisation ? cores) {
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{ pkgs, config, lib, ... }:
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
# maybe upstream this?
|
# maybe upstream this?
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.clan.networking.zerotier;
|
cfg = config.clan.networking.zerotier;
|
||||||
facts = config.clanCore.secrets.zerotier.facts or { };
|
facts = config.clanCore.secrets.zerotier.facts or { };
|
||||||
@ -76,16 +81,18 @@ in
|
|||||||
};
|
};
|
||||||
settings = lib.mkOption {
|
settings = lib.mkOption {
|
||||||
description = lib.mdDoc "override the network config in /var/lib/zerotier/bla/$network.json";
|
description = lib.mdDoc "override the network config in /var/lib/zerotier/bla/$network.json";
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule { freeformType = (pkgs.formats.json { }).type; };
|
||||||
freeformType = (pkgs.formats.json { }).type;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
({
|
({
|
||||||
# Override license so that we can build zerotierone without
|
# Override license so that we can build zerotierone without
|
||||||
# having to re-import nixpkgs.
|
# having to re-import nixpkgs.
|
||||||
services.zerotierone.package = lib.mkDefault (pkgs.zerotierone.overrideAttrs (_old: { meta = { }; }));
|
services.zerotierone.package = lib.mkDefault (
|
||||||
|
pkgs.zerotierone.overrideAttrs (_old: {
|
||||||
|
meta = { };
|
||||||
|
})
|
||||||
|
);
|
||||||
})
|
})
|
||||||
(lib.mkIf ((facts.zerotier-ip.value or null) != null) {
|
(lib.mkIf ((facts.zerotier-ip.value or null) != null) {
|
||||||
environment.etc."zerotier/ip".text = facts.zerotier-ip.value;
|
environment.etc."zerotier/ip".text = facts.zerotier-ip.value;
|
||||||
@ -104,29 +111,33 @@ in
|
|||||||
|
|
||||||
systemd.services.zerotierone.serviceConfig.ExecStartPre = [
|
systemd.services.zerotierone.serviceConfig.ExecStartPre = [
|
||||||
"+${pkgs.writeShellScript "init-zerotier" ''
|
"+${pkgs.writeShellScript "init-zerotier" ''
|
||||||
cp ${config.clanCore.secrets.zerotier.secrets.zerotier-identity-secret.path} /var/lib/zerotier-one/identity.secret
|
cp ${config.clanCore.secrets.zerotier.secrets.zerotier-identity-secret.path} /var/lib/zerotier-one/identity.secret
|
||||||
zerotier-idtool getpublic /var/lib/zerotier-one/identity.secret > /var/lib/zerotier-one/identity.public
|
zerotier-idtool getpublic /var/lib/zerotier-one/identity.secret > /var/lib/zerotier-one/identity.public
|
||||||
|
|
||||||
${lib.optionalString (cfg.controller.enable) ''
|
${lib.optionalString (cfg.controller.enable) ''
|
||||||
mkdir -p /var/lib/zerotier-one/controller.d/network
|
mkdir -p /var/lib/zerotier-one/controller.d/network
|
||||||
ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON cfg.settings)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json
|
ln -sfT ${pkgs.writeText "net.json" (builtins.toJSON cfg.settings)} /var/lib/zerotier-one/controller.d/network/${cfg.networkId}.json
|
||||||
''}
|
''}
|
||||||
${lib.optionalString (cfg.moon.stableEndpoints != []) ''
|
${lib.optionalString (cfg.moon.stableEndpoints != [ ]) ''
|
||||||
if [[ ! -f /var/lib/zerotier-one/moon.json ]]; then
|
if [[ ! -f /var/lib/zerotier-one/moon.json ]]; then
|
||||||
zerotier-idtool initmoon /var/lib/zerotier-one/identity.public > /var/lib/zerotier-one/moon.json
|
zerotier-idtool initmoon /var/lib/zerotier-one/identity.public > /var/lib/zerotier-one/moon.json
|
||||||
fi
|
fi
|
||||||
${genMoonScript}/bin/genmoon /var/lib/zerotier-one/moon.json ${builtins.toFile "moon.json" (builtins.toJSON cfg.moon.stableEndpoints)} /var/lib/zerotier-one/moons.d
|
${genMoonScript}/bin/genmoon /var/lib/zerotier-one/moon.json ${builtins.toFile "moon.json" (builtins.toJSON cfg.moon.stableEndpoints)} /var/lib/zerotier-one/moons.d
|
||||||
''}
|
''}
|
||||||
|
|
||||||
# cleanup old networks
|
# cleanup old networks
|
||||||
if [[ -d /var/lib/zerotier-one/networks.d ]]; then
|
if [[ -d /var/lib/zerotier-one/networks.d ]]; then
|
||||||
find /var/lib/zerotier-one/networks.d \
|
find /var/lib/zerotier-one/networks.d \
|
||||||
-type f \
|
-type f \
|
||||||
-name "*.conf" \
|
-name "*.conf" \
|
||||||
-not \( ${lib.concatMapStringsSep " -o " (netId: ''-name "${netId}.conf"'') config.services.zerotierone.joinNetworks} \) \
|
-not \( ${
|
||||||
-delete
|
lib.concatMapStringsSep " -o " (
|
||||||
fi
|
netId: ''-name "${netId}.conf"''
|
||||||
''}"
|
) config.services.zerotierone.joinNetworks
|
||||||
|
} \) \
|
||||||
|
-delete
|
||||||
|
fi
|
||||||
|
''}"
|
||||||
];
|
];
|
||||||
systemd.services.zerotierone.serviceConfig.ExecStartPost = [
|
systemd.services.zerotierone.serviceConfig.ExecStartPost = [
|
||||||
"+${pkgs.writeShellScript "configure-interface" ''
|
"+${pkgs.writeShellScript "configure-interface" ''
|
||||||
@ -145,7 +156,7 @@ in
|
|||||||
${lib.concatMapStringsSep "\n" (moon: ''
|
${lib.concatMapStringsSep "\n" (moon: ''
|
||||||
zerotier-cli orbit ${moon} ${moon}
|
zerotier-cli orbit ${moon} ${moon}
|
||||||
'') cfg.moon.orbitMoons}
|
'') cfg.moon.orbitMoons}
|
||||||
''}"
|
''}"
|
||||||
];
|
];
|
||||||
|
|
||||||
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 5353 ]; # mdns
|
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 5353 ]; # mdns
|
||||||
@ -172,7 +183,11 @@ in
|
|||||||
facts.zerotier-ip = { };
|
facts.zerotier-ip = { };
|
||||||
facts.zerotier-network-id = { };
|
facts.zerotier-network-id = { };
|
||||||
secrets.zerotier-identity-secret = { };
|
secrets.zerotier-identity-secret = { };
|
||||||
generator.path = [ config.services.zerotierone.package pkgs.fakeroot pkgs.python3 ];
|
generator.path = [
|
||||||
|
config.services.zerotierone.package
|
||||||
|
pkgs.fakeroot
|
||||||
|
pkgs.python3
|
||||||
|
];
|
||||||
generator.script = ''
|
generator.script = ''
|
||||||
python3 ${./generate.py} --mode network \
|
python3 ${./generate.py} --mode network \
|
||||||
--ip "$facts/zerotier-ip" \
|
--ip "$facts/zerotier-ip" \
|
||||||
@ -188,7 +203,10 @@ in
|
|||||||
clanCore.secrets.zerotier = {
|
clanCore.secrets.zerotier = {
|
||||||
facts.zerotier-ip = { };
|
facts.zerotier-ip = { };
|
||||||
secrets.zerotier-identity-secret = { };
|
secrets.zerotier-identity-secret = { };
|
||||||
generator.path = [ config.services.zerotierone.package pkgs.python3 ];
|
generator.path = [
|
||||||
|
config.services.zerotierone.package
|
||||||
|
pkgs.python3
|
||||||
|
];
|
||||||
generator.script = ''
|
generator.script = ''
|
||||||
python3 ${./generate.py} --mode identity \
|
python3 ${./generate.py} --mode identity \
|
||||||
--ip "$facts/zerotier-ip" \
|
--ip "$facts/zerotier-ip" \
|
||||||
@ -200,9 +218,7 @@ in
|
|||||||
(lib.mkIf (cfg.controller.enable && (facts.zerotier-network-id.value or null) != null) {
|
(lib.mkIf (cfg.controller.enable && (facts.zerotier-network-id.value or null) != null) {
|
||||||
clan.networking.zerotier.networkId = facts.zerotier-network-id.value;
|
clan.networking.zerotier.networkId = facts.zerotier-network-id.value;
|
||||||
clan.networking.zerotier.settings = {
|
clan.networking.zerotier.settings = {
|
||||||
authTokens = [
|
authTokens = [ null ];
|
||||||
null
|
|
||||||
];
|
|
||||||
authorizationEndpoint = "";
|
authorizationEndpoint = "";
|
||||||
capabilities = [ ];
|
capabilities = [ ];
|
||||||
clientId = "";
|
clientId = "";
|
||||||
@ -242,7 +258,9 @@ in
|
|||||||
environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value;
|
environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value;
|
||||||
systemd.services.zerotierone.serviceConfig.ExecStartPost = [
|
systemd.services.zerotierone.serviceConfig.ExecStartPost = [
|
||||||
"+${pkgs.writeShellScript "whitelist-controller" ''
|
"+${pkgs.writeShellScript "whitelist-controller" ''
|
||||||
${config.clanCore.clanPkgs.zerotier-members}/bin/zerotier-members allow ${builtins.substring 0 10 cfg.networkId}
|
${config.clanCore.clanPkgs.zerotier-members}/bin/zerotier-members allow ${
|
||||||
|
builtins.substring 0 10 cfg.networkId
|
||||||
|
}
|
||||||
''}"
|
''}"
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ inputs, self, ... }: {
|
{ inputs, self, ... }:
|
||||||
|
{
|
||||||
flake.nixosModules = {
|
flake.nixosModules = {
|
||||||
hidden-ssh-announce.imports = [ ./hidden-ssh-announce.nix ];
|
hidden-ssh-announce.imports = [ ./hidden-ssh-announce.nix ];
|
||||||
installer.imports = [
|
installer.imports = [
|
||||||
@ -10,9 +11,12 @@
|
|||||||
inputs.sops-nix.nixosModules.sops
|
inputs.sops-nix.nixosModules.sops
|
||||||
./clanCore
|
./clanCore
|
||||||
./iso
|
./iso
|
||||||
({ pkgs, lib, ... }: {
|
(
|
||||||
clanCore.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system};
|
{ pkgs, lib, ... }:
|
||||||
})
|
{
|
||||||
|
clanCore.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system};
|
||||||
|
}
|
||||||
|
)
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
{ config
|
{
|
||||||
, lib
|
config,
|
||||||
, pkgs
|
lib,
|
||||||
, ...
|
pkgs,
|
||||||
}: {
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
options.hidden-ssh-announce = {
|
options.hidden-ssh-announce = {
|
||||||
enable = lib.mkEnableOption "hidden-ssh-announce";
|
enable = lib.mkEnableOption "hidden-ssh-announce";
|
||||||
script = lib.mkOption {
|
script = lib.mkOption {
|
||||||
@ -32,8 +34,14 @@
|
|||||||
};
|
};
|
||||||
systemd.services.hidden-ssh-announce = {
|
systemd.services.hidden-ssh-announce = {
|
||||||
description = "announce hidden ssh";
|
description = "announce hidden ssh";
|
||||||
after = [ "tor.service" "network-online.target" ];
|
after = [
|
||||||
wants = [ "tor.service" "network-online.target" ];
|
"tor.service"
|
||||||
|
"network-online.target"
|
||||||
|
];
|
||||||
|
wants = [
|
||||||
|
"tor.service"
|
||||||
|
"network-online.target"
|
||||||
|
];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
# ${pkgs.tor}/bin/torify
|
# ${pkgs.tor}/bin/torify
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{ lib
|
{
|
||||||
, pkgs
|
lib,
|
||||||
, modulesPath
|
pkgs,
|
||||||
, ...
|
modulesPath,
|
||||||
}: {
|
...
|
||||||
systemd.tmpfiles.rules = [
|
}:
|
||||||
"d /var/shared 0777 root root - -"
|
{
|
||||||
];
|
systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/profiles/installation-device.nix")
|
(modulesPath + "/profiles/installation-device.nix")
|
||||||
(modulesPath + "/profiles/all-hardware.nix")
|
(modulesPath + "/profiles/all-hardware.nix")
|
||||||
@ -21,7 +21,17 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
script = pkgs.writeShellScript "write-hostname" ''
|
script = pkgs.writeShellScript "write-hostname" ''
|
||||||
set -efu
|
set -efu
|
||||||
export PATH=${lib.makeBinPath (with pkgs; [ iproute2 coreutils jq qrencode ])}
|
export PATH=${
|
||||||
|
lib.makeBinPath (
|
||||||
|
with pkgs;
|
||||||
|
[
|
||||||
|
iproute2
|
||||||
|
coreutils
|
||||||
|
jq
|
||||||
|
qrencode
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
mkdir -p /var/shared
|
mkdir -p /var/shared
|
||||||
echo "$1" > /var/shared/onion-hostname
|
echo "$1" > /var/shared/onion-hostname
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
{ config, extendModules, lib, pkgs, ... }:
|
{
|
||||||
|
config,
|
||||||
|
extendModules,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
# Generates a fileSystems entry for bind mounting a given state folder path
|
# Generates a fileSystems entry for bind mounting a given state folder path
|
||||||
# It binds directories from /var/clanstate/{some-path} to /{some-path}.
|
# It binds directories from /var/clanstate/{some-path} to /{some-path}.
|
||||||
@ -13,54 +19,47 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Flatten the list of state folders into a single list
|
# Flatten the list of state folders into a single list
|
||||||
stateFolders = lib.flatten (
|
stateFolders = lib.flatten (lib.mapAttrsToList (_item: attrs: attrs.folders) config.clanCore.state);
|
||||||
lib.mapAttrsToList
|
|
||||||
(_item: attrs: attrs.folders)
|
|
||||||
config.clanCore.state
|
|
||||||
);
|
|
||||||
|
|
||||||
# A module setting up bind mounts for all state folders
|
# A module setting up bind mounts for all state folders
|
||||||
stateMounts = {
|
stateMounts = {
|
||||||
fileSystems =
|
fileSystems = lib.listToAttrs (map mkBindMount stateFolders);
|
||||||
lib.listToAttrs
|
|
||||||
(map mkBindMount stateFolders);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
isoModule = { config, ... }: {
|
isoModule =
|
||||||
imports = [
|
{ config, ... }:
|
||||||
stateMounts
|
{
|
||||||
];
|
imports = [ stateMounts ];
|
||||||
options.clan.iso.disko = lib.mkOption {
|
options.clan.iso.disko = lib.mkOption {
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule { freeformType = (pkgs.formats.json { }).type; };
|
||||||
freeformType = (pkgs.formats.json { }).type;
|
default = {
|
||||||
};
|
disk = {
|
||||||
default = {
|
iso = {
|
||||||
disk = {
|
type = "disk";
|
||||||
iso = {
|
imageSize = "10G"; # TODO add auto image size in disko
|
||||||
type = "disk";
|
content = {
|
||||||
imageSize = "10G"; # TODO add auto image size in disko
|
type = "gpt";
|
||||||
content = {
|
partitions = {
|
||||||
type = "gpt";
|
boot = {
|
||||||
partitions = {
|
size = "1M";
|
||||||
boot = {
|
type = "EF02"; # for grub MBR
|
||||||
size = "1M";
|
|
||||||
type = "EF02"; # for grub MBR
|
|
||||||
};
|
|
||||||
ESP = {
|
|
||||||
size = "100M";
|
|
||||||
type = "EF00";
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "vfat";
|
|
||||||
mountpoint = "/boot";
|
|
||||||
};
|
};
|
||||||
};
|
ESP = {
|
||||||
root = {
|
size = "100M";
|
||||||
size = "100%";
|
type = "EF00";
|
||||||
content = {
|
content = {
|
||||||
type = "filesystem";
|
type = "filesystem";
|
||||||
format = "ext4";
|
format = "vfat";
|
||||||
mountpoint = "/";
|
mountpoint = "/boot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -68,19 +67,16 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
config = {
|
||||||
|
disko.devices = lib.mkOverride 51 config.clan.iso.disko;
|
||||||
|
boot.loader.grub.enable = true;
|
||||||
|
boot.loader.grub.efiSupport = true;
|
||||||
|
boot.loader.grub.device = lib.mkForce "/dev/vda";
|
||||||
|
boot.loader.grub.efiInstallAsRemovable = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
config = {
|
|
||||||
disko.devices = lib.mkOverride 51 config.clan.iso.disko;
|
|
||||||
boot.loader.grub.enable = true;
|
|
||||||
boot.loader.grub.efiSupport = true;
|
|
||||||
boot.loader.grub.device = lib.mkForce "/dev/vda";
|
|
||||||
boot.loader.grub.efiInstallAsRemovable = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
isoConfig = extendModules {
|
isoConfig = extendModules { modules = [ isoModule ]; };
|
||||||
modules = [ isoModule ];
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
|
@ -1,36 +1,37 @@
|
|||||||
{ age
|
{
|
||||||
, lib
|
age,
|
||||||
, argcomplete
|
lib,
|
||||||
, installShellFiles
|
argcomplete,
|
||||||
, nix
|
installShellFiles,
|
||||||
, openssh
|
nix,
|
||||||
, pytest
|
openssh,
|
||||||
, pytest-cov
|
pytest,
|
||||||
, pytest-xdist
|
pytest-cov,
|
||||||
, pytest-subprocess
|
pytest-xdist,
|
||||||
, pytest-timeout
|
pytest-subprocess,
|
||||||
, remote-pdb
|
pytest-timeout,
|
||||||
, ipdb
|
remote-pdb,
|
||||||
, python3
|
ipdb,
|
||||||
, runCommand
|
python3,
|
||||||
, setuptools
|
runCommand,
|
||||||
, sops
|
setuptools,
|
||||||
, stdenv
|
sops,
|
||||||
, wheel
|
stdenv,
|
||||||
, fakeroot
|
wheel,
|
||||||
, rsync
|
fakeroot,
|
||||||
, bash
|
rsync,
|
||||||
, sshpass
|
bash,
|
||||||
, zbar
|
sshpass,
|
||||||
, tor
|
zbar,
|
||||||
, git
|
tor,
|
||||||
, nixpkgs
|
git,
|
||||||
, qemu
|
nixpkgs,
|
||||||
, gnupg
|
qemu,
|
||||||
, e2fsprogs
|
gnupg,
|
||||||
, mypy
|
e2fsprogs,
|
||||||
, rope
|
mypy,
|
||||||
, clan-core-path
|
rope,
|
||||||
|
clan-core-path,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
|
||||||
@ -38,19 +39,22 @@ let
|
|||||||
argcomplete # optional dependency: if not enabled, shell completion will not work
|
argcomplete # optional dependency: if not enabled, shell completion will not work
|
||||||
];
|
];
|
||||||
|
|
||||||
pytestDependencies = runtimeDependencies ++ dependencies ++ [
|
pytestDependencies =
|
||||||
pytest
|
runtimeDependencies
|
||||||
pytest-cov
|
++ dependencies
|
||||||
pytest-subprocess
|
++ [
|
||||||
pytest-xdist
|
pytest
|
||||||
pytest-timeout
|
pytest-cov
|
||||||
remote-pdb
|
pytest-subprocess
|
||||||
ipdb
|
pytest-xdist
|
||||||
openssh
|
pytest-timeout
|
||||||
git
|
remote-pdb
|
||||||
gnupg
|
ipdb
|
||||||
stdenv.cc
|
openssh
|
||||||
];
|
git
|
||||||
|
gnupg
|
||||||
|
stdenv.cc
|
||||||
|
];
|
||||||
|
|
||||||
# Optional dependencies for clan cli, we re-expose them here to make sure they all build.
|
# Optional dependencies for clan cli, we re-expose them here to make sure they all build.
|
||||||
runtimeDependencies = [
|
runtimeDependencies = [
|
||||||
@ -70,7 +74,9 @@ let
|
|||||||
e2fsprogs
|
e2fsprogs
|
||||||
];
|
];
|
||||||
|
|
||||||
runtimeDependenciesAsSet = builtins.listToAttrs (builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies);
|
runtimeDependenciesAsSet = builtins.listToAttrs (
|
||||||
|
builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies
|
||||||
|
);
|
||||||
|
|
||||||
checkPython = python3.withPackages (_ps: pytestDependencies);
|
checkPython = python3.withPackages (_ps: pytestDependencies);
|
||||||
|
|
||||||
@ -121,42 +127,48 @@ python3.pkgs.buildPythonApplication {
|
|||||||
propagatedBuildInputs = dependencies;
|
propagatedBuildInputs = dependencies;
|
||||||
|
|
||||||
# also re-expose dependencies so we test them in CI
|
# also re-expose dependencies so we test them in CI
|
||||||
passthru.tests = (lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet) // rec {
|
passthru.tests =
|
||||||
clan-pytest-without-core = runCommand "clan-pytest-without-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } ''
|
(lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet)
|
||||||
cp -r ${source} ./src
|
// rec {
|
||||||
chmod +w -R ./src
|
clan-pytest-without-core =
|
||||||
cd ./src
|
runCommand "clan-pytest-without-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; }
|
||||||
|
''
|
||||||
|
cp -r ${source} ./src
|
||||||
|
chmod +w -R ./src
|
||||||
|
cd ./src
|
||||||
|
|
||||||
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
||||||
${checkPython}/bin/python -m pytest -m "not impure and not with_core" ./tests
|
${checkPython}/bin/python -m pytest -m "not impure and not with_core" ./tests
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
# separate the tests that can never be cached
|
# separate the tests that can never be cached
|
||||||
clan-pytest-with-core = runCommand "clan-pytest-with-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } ''
|
clan-pytest-with-core =
|
||||||
cp -r ${source} ./src
|
runCommand "clan-pytest-with-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; }
|
||||||
chmod +w -R ./src
|
''
|
||||||
cd ./src
|
cp -r ${source} ./src
|
||||||
|
chmod +w -R ./src
|
||||||
|
cd ./src
|
||||||
|
|
||||||
export CLAN_CORE=${clan-core-path}
|
export CLAN_CORE=${clan-core-path}
|
||||||
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
||||||
${checkPython}/bin/python -m pytest -m "not impure and with_core" ./tests
|
${checkPython}/bin/python -m pytest -m "not impure and with_core" ./tests
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
clan-pytest = runCommand "clan-pytest" { } ''
|
clan-pytest = runCommand "clan-pytest" { } ''
|
||||||
echo ${clan-pytest-without-core}
|
echo ${clan-pytest-without-core}
|
||||||
echo ${clan-pytest-with-core}
|
echo ${clan-pytest-with-core}
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
check-for-breakpoints = runCommand "breakpoints" { } ''
|
check-for-breakpoints = runCommand "breakpoints" { } ''
|
||||||
if grep --include \*.py -Rq "breakpoint()" ${source}; then
|
if grep --include \*.py -Rq "breakpoint()" ${source}; then
|
||||||
echo "breakpoint() found in ${source}:"
|
echo "breakpoint() found in ${source}:"
|
||||||
grep --include \*.py -Rn "breakpoint()" ${source}
|
grep --include \*.py -Rn "breakpoint()" ${source}
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
passthru.nixpkgs = nixpkgs';
|
passthru.nixpkgs = nixpkgs';
|
||||||
passthru.checkPython = checkPython;
|
passthru.checkPython = checkPython;
|
||||||
|
@ -1,37 +1,44 @@
|
|||||||
{ inputs, self, lib, ... }:
|
|
||||||
{
|
{
|
||||||
perSystem = { self', pkgs, ... }:
|
inputs,
|
||||||
|
self,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{ self', pkgs, ... }:
|
||||||
let
|
let
|
||||||
flakeLock = lib.importJSON (self + /flake.lock);
|
flakeLock = lib.importJSON (self + /flake.lock);
|
||||||
flakeInputs = (builtins.removeAttrs inputs [ "self" ]);
|
flakeInputs = (builtins.removeAttrs inputs [ "self" ]);
|
||||||
flakeLockVendoredDeps = flakeLock // {
|
flakeLockVendoredDeps = flakeLock // {
|
||||||
nodes = flakeLock.nodes // (
|
nodes =
|
||||||
lib.flip lib.mapAttrs flakeInputs (name: _: flakeLock.nodes.${name} // {
|
flakeLock.nodes
|
||||||
locked = {
|
// (lib.flip lib.mapAttrs flakeInputs (
|
||||||
inherit (flakeLock.nodes.${name}.locked) narHash;
|
name: _:
|
||||||
lastModified =
|
flakeLock.nodes.${name}
|
||||||
# lol, nixpkgs has a different timestamp on the fs???
|
// {
|
||||||
if name == "nixpkgs"
|
locked = {
|
||||||
then 0
|
inherit (flakeLock.nodes.${name}.locked) narHash;
|
||||||
else 1;
|
lastModified =
|
||||||
path = "${inputs.${name}}";
|
# lol, nixpkgs has a different timestamp on the fs???
|
||||||
type = "path";
|
if name == "nixpkgs" then 0 else 1;
|
||||||
};
|
path = "${inputs.${name}}";
|
||||||
})
|
type = "path";
|
||||||
);
|
};
|
||||||
|
}
|
||||||
|
));
|
||||||
};
|
};
|
||||||
flakeLockFile = builtins.toFile "clan-core-flake.lock"
|
flakeLockFile = builtins.toFile "clan-core-flake.lock" (builtins.toJSON flakeLockVendoredDeps);
|
||||||
(builtins.toJSON flakeLockVendoredDeps);
|
clanCoreWithVendoredDeps =
|
||||||
clanCoreWithVendoredDeps = lib.trace flakeLockFile pkgs.runCommand "clan-core-with-vendored-deps" { } ''
|
lib.trace flakeLockFile pkgs.runCommand "clan-core-with-vendored-deps" { }
|
||||||
cp -r ${self} $out
|
''
|
||||||
chmod +w -R $out
|
cp -r ${self} $out
|
||||||
cp ${flakeLockFile} $out/flake.lock
|
chmod +w -R $out
|
||||||
'';
|
cp ${flakeLockFile} $out/flake.lock
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
devShells.clan-cli = pkgs.callPackage ./shell.nix {
|
devShells.clan-cli = pkgs.callPackage ./shell.nix { inherit (self'.packages) clan-cli; };
|
||||||
inherit (self'.packages) clan-cli;
|
|
||||||
};
|
|
||||||
packages = {
|
packages = {
|
||||||
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
|
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
inherit (inputs) nixpkgs;
|
inherit (inputs) nixpkgs;
|
||||||
@ -42,5 +49,4 @@
|
|||||||
|
|
||||||
checks = self'.packages.clan-cli.tests;
|
checks = self'.packages.clan-cli.tests;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
{ nix-unit, clan-cli, system, mkShell, writeScriptBin, openssh, ruff, python3 }:
|
{
|
||||||
|
nix-unit,
|
||||||
|
clan-cli,
|
||||||
|
system,
|
||||||
|
mkShell,
|
||||||
|
writeScriptBin,
|
||||||
|
openssh,
|
||||||
|
ruff,
|
||||||
|
python3,
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
checkScript = writeScriptBin "check" ''
|
checkScript = writeScriptBin "check" ''
|
||||||
nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@"
|
nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
pythonWithDeps = python3.withPackages (
|
pythonWithDeps = python3.withPackages (
|
||||||
ps:
|
ps: clan-cli.propagatedBuildInputs ++ clan-cli.devDependencies ++ [ ps.pip ]
|
||||||
clan-cli.propagatedBuildInputs
|
|
||||||
++ clan-cli.devDependencies
|
|
||||||
++ [
|
|
||||||
ps.pip
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
mkShell {
|
mkShell {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ lib, ... }: {
|
{ lib, ... }:
|
||||||
|
{
|
||||||
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
||||||
system.stateVersion = lib.version;
|
system.stateVersion = lib.version;
|
||||||
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ lib, ... }: {
|
{ lib, ... }:
|
||||||
|
{
|
||||||
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
||||||
system.stateVersion = lib.version;
|
system.stateVersion = lib.version;
|
||||||
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ lib, ... }: {
|
{ lib, ... }:
|
||||||
|
{
|
||||||
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
||||||
system.stateVersion = lib.version;
|
system.stateVersion = lib.version;
|
||||||
clan.virtualisation.graphics = false;
|
clan.virtualisation.graphics = false;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
{ lib
|
{ lib, ... }:
|
||||||
, ...
|
{
|
||||||
}: {
|
|
||||||
options.clan.fake-module.fake-flag = lib.mkOption {
|
options.clan.fake-module.fake-flag = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
@ -2,32 +2,41 @@
|
|||||||
# this placeholder is replaced by the path to nixpkgs
|
# this placeholder is replaced by the path to nixpkgs
|
||||||
inputs.nixpkgs.url = "__NIXPKGS__";
|
inputs.nixpkgs.url = "__NIXPKGS__";
|
||||||
|
|
||||||
outputs = inputs':
|
outputs =
|
||||||
|
inputs':
|
||||||
let
|
let
|
||||||
# fake clan-core input
|
# fake clan-core input
|
||||||
fake-clan-core = {
|
fake-clan-core = {
|
||||||
clanModules.fake-module = ./fake-module.nix;
|
clanModules.fake-module = ./fake-module.nix;
|
||||||
};
|
};
|
||||||
inputs = inputs' // { clan-core = fake-clan-core; };
|
inputs = inputs' // {
|
||||||
|
clan-core = fake-clan-core;
|
||||||
|
};
|
||||||
machineSettings = (
|
machineSettings = (
|
||||||
if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != ""
|
if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" then
|
||||||
then builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE"))
|
builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE"))
|
||||||
else if builtins.pathExists ./machines/machine1/settings.json
|
else if builtins.pathExists ./machines/machine1/settings.json then
|
||||||
then builtins.fromJSON (builtins.readFile ./machines/machine1/settings.json)
|
builtins.fromJSON (builtins.readFile ./machines/machine1/settings.json)
|
||||||
else { }
|
else
|
||||||
|
{ }
|
||||||
|
);
|
||||||
|
machineImports = map (module: fake-clan-core.clanModules.${module}) (
|
||||||
|
machineSettings.clanImports or [ ]
|
||||||
);
|
);
|
||||||
machineImports =
|
|
||||||
map
|
|
||||||
(module: fake-clan-core.clanModules.${module})
|
|
||||||
(machineSettings.clanImports or [ ]);
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
nixosConfigurations.machine1 = inputs.nixpkgs.lib.nixosSystem {
|
nixosConfigurations.machine1 = inputs.nixpkgs.lib.nixosSystem {
|
||||||
modules =
|
modules = machineImports ++ [
|
||||||
machineImports ++ [
|
./nixosModules/machine1.nix
|
||||||
./nixosModules/machine1.nix
|
machineSettings
|
||||||
machineSettings
|
(
|
||||||
({ lib, options, pkgs, ... }: {
|
{
|
||||||
|
lib,
|
||||||
|
options,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
nixpkgs.hostPlatform = "x86_64-linux";
|
nixpkgs.hostPlatform = "x86_64-linux";
|
||||||
# speed up by not instantiating nixpkgs twice and disable documentation
|
# speed up by not instantiating nixpkgs twice and disable documentation
|
||||||
@ -51,8 +60,9 @@
|
|||||||
The buildClan function will automatically import these modules for the current machine.
|
The buildClan function will automatically import these modules for the current machine.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
})
|
}
|
||||||
];
|
)
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ lib, ... }: {
|
{ lib, ... }:
|
||||||
|
{
|
||||||
options.clan.jitsi.enable = lib.mkOption {
|
options.clan.jitsi.enable = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
@ -5,40 +5,45 @@
|
|||||||
# this placeholder is replaced by the path to nixpkgs
|
# this placeholder is replaced by the path to nixpkgs
|
||||||
inputs.clan-core.url = "__CLAN_CORE__";
|
inputs.clan-core.url = "__CLAN_CORE__";
|
||||||
|
|
||||||
outputs = { self, clan-core }:
|
outputs =
|
||||||
|
{ self, clan-core }:
|
||||||
let
|
let
|
||||||
clan = clan-core.lib.buildClan {
|
clan = clan-core.lib.buildClan {
|
||||||
directory = self;
|
directory = self;
|
||||||
clanName = "test_flake_with_core";
|
clanName = "test_flake_with_core";
|
||||||
machines = {
|
machines = {
|
||||||
vm1 = { lib, ... }: {
|
vm1 =
|
||||||
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
{ lib, ... }:
|
||||||
system.stateVersion = lib.version;
|
{
|
||||||
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
||||||
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";
|
system.stateVersion = lib.version;
|
||||||
clanCore.sops.defaultGroups = [ "admins" ];
|
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
||||||
clan.virtualisation.graphics = false;
|
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";
|
||||||
|
clanCore.sops.defaultGroups = [ "admins" ];
|
||||||
|
clan.virtualisation.graphics = false;
|
||||||
|
|
||||||
clan.networking.zerotier.controller.enable = true;
|
clan.networking.zerotier.controller.enable = true;
|
||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
|
|
||||||
systemd.services.shutdown-after-boot = {
|
systemd.services.shutdown-after-boot = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "multi-user.target" ];
|
after = [ "multi-user.target" ];
|
||||||
script = ''
|
script = ''
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
shutdown -h now
|
shutdown -h now
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
vm2 =
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
||||||
|
system.stateVersion = lib.version;
|
||||||
|
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
||||||
|
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";
|
||||||
|
clan.networking.zerotier.networkId = "82b44b162ec6c013";
|
||||||
};
|
};
|
||||||
};
|
|
||||||
vm2 = { lib, ... }: {
|
|
||||||
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
|
||||||
system.stateVersion = lib.version;
|
|
||||||
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
|
|
||||||
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";
|
|
||||||
clan.networking.zerotier.networkId = "82b44b162ec6c013";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
@ -5,30 +5,33 @@
|
|||||||
# this placeholder is replaced by the path to clan-core
|
# this placeholder is replaced by the path to clan-core
|
||||||
inputs.clan-core.url = "__CLAN_CORE__";
|
inputs.clan-core.url = "__CLAN_CORE__";
|
||||||
|
|
||||||
outputs = { self, clan-core }:
|
outputs =
|
||||||
|
{ self, clan-core }:
|
||||||
let
|
let
|
||||||
clan = clan-core.lib.buildClan {
|
clan = clan-core.lib.buildClan {
|
||||||
directory = self;
|
directory = self;
|
||||||
clanName = "test_flake_with_core_and_pass";
|
clanName = "test_flake_with_core_and_pass";
|
||||||
machines = {
|
machines = {
|
||||||
vm1 = { lib, ... }: {
|
vm1 =
|
||||||
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
{ lib, ... }:
|
||||||
system.stateVersion = lib.version;
|
{
|
||||||
clanCore.secretStore = "password-store";
|
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
|
||||||
clanCore.secretsUploadDirectory = lib.mkForce "__CLAN_SOPS_KEY_DIR__/secrets";
|
system.stateVersion = lib.version;
|
||||||
|
clanCore.secretStore = "password-store";
|
||||||
|
clanCore.secretsUploadDirectory = lib.mkForce "__CLAN_SOPS_KEY_DIR__/secrets";
|
||||||
|
|
||||||
clan.networking.zerotier.controller.enable = true;
|
clan.networking.zerotier.controller.enable = true;
|
||||||
|
|
||||||
systemd.services.shutdown-after-boot = {
|
systemd.services.shutdown-after-boot = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "multi-user.target" ];
|
after = [ "multi-user.target" ];
|
||||||
script = ''
|
script = ''
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
shutdown -h now
|
shutdown -h now
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
# this placeholder is replaced by the path to nixpkgs
|
# this placeholder is replaced by the path to nixpkgs
|
||||||
inputs.clan-core.url = "__CLAN_CORE__";
|
inputs.clan-core.url = "__CLAN_CORE__";
|
||||||
|
|
||||||
outputs = { self, clan-core }:
|
outputs =
|
||||||
|
{ self, clan-core }:
|
||||||
let
|
let
|
||||||
clan = clan-core.lib.buildClan {
|
clan = clan-core.lib.buildClan {
|
||||||
directory = self;
|
directory = self;
|
||||||
@ -14,9 +15,7 @@
|
|||||||
let
|
let
|
||||||
machineModules = builtins.readDir (self + "/machines");
|
machineModules = builtins.readDir (self + "/machines");
|
||||||
in
|
in
|
||||||
builtins.mapAttrs
|
builtins.mapAttrs (name: _type: import (self + "/machines/${name}")) machineModules;
|
||||||
(name: _type: import (self + "/machines/${name}"))
|
|
||||||
machineModules;
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
{ python3
|
{
|
||||||
, runCommand
|
python3,
|
||||||
, setuptools
|
runCommand,
|
||||||
, copyDesktopItems
|
setuptools,
|
||||||
, pygobject3
|
copyDesktopItems,
|
||||||
, wrapGAppsHook
|
pygobject3,
|
||||||
, gtk4
|
wrapGAppsHook,
|
||||||
, gnome
|
gtk4,
|
||||||
, pygobject-stubs
|
gnome,
|
||||||
, gobject-introspection
|
pygobject-stubs,
|
||||||
, clan-cli
|
gobject-introspection,
|
||||||
, makeDesktopItem
|
clan-cli,
|
||||||
, libadwaita
|
makeDesktopItem,
|
||||||
|
libadwaita,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
source = ./.;
|
source = ./.;
|
||||||
@ -41,7 +42,11 @@ python3.pkgs.buildPythonApplication {
|
|||||||
gobject-introspection
|
gobject-introspection
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [ gtk4 libadwaita gnome.adwaita-icon-theme ];
|
buildInputs = [
|
||||||
|
gtk4
|
||||||
|
libadwaita
|
||||||
|
gnome.adwaita-icon-theme
|
||||||
|
];
|
||||||
|
|
||||||
# We need to propagate the build inputs to nix fmt / treefmt
|
# We need to propagate the build inputs to nix fmt / treefmt
|
||||||
propagatedBuildInputs = [
|
propagatedBuildInputs = [
|
||||||
@ -73,7 +78,5 @@ python3.pkgs.buildPythonApplication {
|
|||||||
checkPhase = ''
|
checkPhase = ''
|
||||||
PYTHONPATH= $out/bin/clan-vm-manager --help
|
PYTHONPATH= $out/bin/clan-vm-manager --help
|
||||||
'';
|
'';
|
||||||
desktopItems = [
|
desktopItems = [ desktop-file ];
|
||||||
desktop-file
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
{ ... }: {
|
{ ... }:
|
||||||
perSystem = { config, pkgs, ... }: {
|
{
|
||||||
devShells.clan-vm-manager = pkgs.callPackage ./shell.nix {
|
perSystem =
|
||||||
inherit (config.packages) clan-cli clan-vm-manager;
|
{ config, pkgs, ... }:
|
||||||
};
|
{
|
||||||
packages.clan-vm-manager = pkgs.python3.pkgs.callPackage ./default.nix {
|
devShells.clan-vm-manager = pkgs.callPackage ./shell.nix {
|
||||||
inherit (config.packages) clan-cli;
|
inherit (config.packages) clan-cli clan-vm-manager;
|
||||||
};
|
};
|
||||||
|
packages.clan-vm-manager = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
|
inherit (config.packages) clan-cli;
|
||||||
|
};
|
||||||
|
|
||||||
checks = config.packages.clan-vm-manager.tests;
|
checks = config.packages.clan-vm-manager.tests;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,37 @@
|
|||||||
{ lib, runCommand, makeWrapper, stdenv, clan-vm-manager, gdb, gtk4, libadwaita, clan-cli, mkShell, ruff, desktop-file-utils, xdg-utils, mypy, python3, python3Packages }:
|
{
|
||||||
|
lib,
|
||||||
|
runCommand,
|
||||||
|
makeWrapper,
|
||||||
|
stdenv,
|
||||||
|
clan-vm-manager,
|
||||||
|
gdb,
|
||||||
|
gtk4,
|
||||||
|
libadwaita,
|
||||||
|
clan-cli,
|
||||||
|
mkShell,
|
||||||
|
ruff,
|
||||||
|
desktop-file-utils,
|
||||||
|
xdg-utils,
|
||||||
|
mypy,
|
||||||
|
python3,
|
||||||
|
python3Packages,
|
||||||
|
}:
|
||||||
mkShell (
|
mkShell (
|
||||||
let
|
let
|
||||||
pygdb = runCommand "pygdb" { buildInputs = [ gdb python3 makeWrapper ]; } ''
|
pygdb =
|
||||||
mkdir -p "$out/bin"
|
runCommand "pygdb"
|
||||||
makeWrapper "${gdb}/bin/gdb" "$out/bin/pygdb" \
|
{
|
||||||
--add-flags '-ex "source ${python3}/share/gdb/libpython.py"'
|
buildInputs = [
|
||||||
'';
|
gdb
|
||||||
|
python3
|
||||||
|
makeWrapper
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkdir -p "$out/bin"
|
||||||
|
makeWrapper "${gdb}/bin/gdb" "$out/bin/pygdb" \
|
||||||
|
--add-flags '-ex "source ${python3}/share/gdb/libpython.py"'
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
inherit (clan-vm-manager) propagatedBuildInputs buildInputs;
|
inherit (clan-vm-manager) propagatedBuildInputs buildInputs;
|
||||||
@ -15,7 +41,6 @@ mkShell (
|
|||||||
pygdb
|
pygdb
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
# To debug clan-vm-manger execute pygdb --args python ./bin/clan-vm-manager
|
# To debug clan-vm-manger execute pygdb --args python ./bin/clan-vm-manager
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
ruff
|
ruff
|
||||||
|
@ -1,38 +1,45 @@
|
|||||||
{ ... }: {
|
{ ... }:
|
||||||
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./clan-cli/flake-module.nix
|
./clan-cli/flake-module.nix
|
||||||
./clan-vm-manager/flake-module.nix
|
./clan-vm-manager/flake-module.nix
|
||||||
./installer/flake-module.nix
|
./installer/flake-module.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
perSystem = { pkgs, config, lib, ... }: {
|
perSystem =
|
||||||
packages = {
|
{
|
||||||
tea-create-pr = pkgs.callPackage ./tea-create-pr { };
|
pkgs,
|
||||||
zerotier-members = pkgs.callPackage ./zerotier-members { };
|
config,
|
||||||
zt-tcp-relay = pkgs.callPackage ./zt-tcp-relay { };
|
lib,
|
||||||
merge-after-ci = pkgs.callPackage ./merge-after-ci {
|
...
|
||||||
inherit (config.packages) tea-create-pr;
|
}:
|
||||||
};
|
{
|
||||||
pending-reviews = pkgs.callPackage ./pending-reviews { };
|
packages =
|
||||||
} // lib.optionalAttrs pkgs.stdenv.isLinux {
|
{
|
||||||
wayland-proxy-virtwl = pkgs.callPackage ./wayland-proxy-virtwl { };
|
tea-create-pr = pkgs.callPackage ./tea-create-pr { };
|
||||||
waypipe = pkgs.waypipe.overrideAttrs
|
zerotier-members = pkgs.callPackage ./zerotier-members { };
|
||||||
(_old: {
|
zt-tcp-relay = pkgs.callPackage ./zt-tcp-relay { };
|
||||||
# https://gitlab.freedesktop.org/mstoeckl/waypipe
|
merge-after-ci = pkgs.callPackage ./merge-after-ci { inherit (config.packages) tea-create-pr; };
|
||||||
src = pkgs.fetchFromGitLab {
|
pending-reviews = pkgs.callPackage ./pending-reviews { };
|
||||||
domain = "gitlab.freedesktop.org";
|
}
|
||||||
owner = "mstoeckl";
|
// lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||||
repo = "waypipe";
|
wayland-proxy-virtwl = pkgs.callPackage ./wayland-proxy-virtwl { };
|
||||||
rev = "4e4ff3bc1943cf7f6aeb56b06c060f40578d3570";
|
waypipe = pkgs.waypipe.overrideAttrs (_old: {
|
||||||
hash = "sha256-dxz4AmeJAweffyPCayvykworQNntHtHeq6PXMXWsM5k=";
|
# https://gitlab.freedesktop.org/mstoeckl/waypipe
|
||||||
};
|
src = pkgs.fetchFromGitLab {
|
||||||
});
|
domain = "gitlab.freedesktop.org";
|
||||||
# halalify zerotierone
|
owner = "mstoeckl";
|
||||||
zerotierone = pkgs.zerotierone.overrideAttrs (_old: {
|
repo = "waypipe";
|
||||||
meta = _old.meta // {
|
rev = "4e4ff3bc1943cf7f6aeb56b06c060f40578d3570";
|
||||||
license = lib.licenses.apsl20;
|
hash = "sha256-dxz4AmeJAweffyPCayvykworQNntHtHeq6PXMXWsM5k=";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
# halalify zerotierone
|
||||||
|
zerotierone = pkgs.zerotierone.overrideAttrs (_old: {
|
||||||
|
meta = _old.meta // {
|
||||||
|
license = lib.licenses.apsl20;
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{ lib
|
{
|
||||||
, buildGoModule
|
lib,
|
||||||
, fetchFromGitHub
|
buildGoModule,
|
||||||
,
|
fetchFromGitHub,
|
||||||
}:
|
}:
|
||||||
buildGoModule rec {
|
buildGoModule rec {
|
||||||
pname = "go-ssb";
|
pname = "go-ssb";
|
||||||
@ -17,7 +17,10 @@ buildGoModule rec {
|
|||||||
|
|
||||||
vendorHash = "sha256-ZytuWFre7Cz6Qt01tLQoPEuNzDIyoC938OkdIrU8nZo=";
|
vendorHash = "sha256-ZytuWFre7Cz6Qt01tLQoPEuNzDIyoC938OkdIrU8nZo=";
|
||||||
|
|
||||||
ldflags = [ "-s" "-w" ];
|
ldflags = [
|
||||||
|
"-s"
|
||||||
|
"-w"
|
||||||
|
];
|
||||||
|
|
||||||
# take very long
|
# take very long
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
{ self, lib, ... }:
|
{ self, lib, ... }:
|
||||||
let
|
let
|
||||||
installerModule = { config, pkgs, ... }: {
|
installerModule =
|
||||||
imports = [
|
{ config, pkgs, ... }:
|
||||||
self.nixosModules.installer
|
{
|
||||||
self.inputs.nixos-generators.nixosModules.all-formats
|
imports = [
|
||||||
];
|
self.nixosModules.installer
|
||||||
|
self.inputs.nixos-generators.nixosModules.all-formats
|
||||||
|
];
|
||||||
|
|
||||||
system.stateVersion = config.system.nixos.version;
|
system.stateVersion = config.system.nixos.version;
|
||||||
nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux;
|
nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux;
|
||||||
};
|
};
|
||||||
|
|
||||||
installer = lib.nixosSystem {
|
installer = lib.nixosSystem {
|
||||||
modules = [
|
modules = [
|
||||||
@ -27,7 +29,9 @@ in
|
|||||||
flake.packages.x86_64-linux.install-iso = self.inputs.disko.lib.makeDiskImages {
|
flake.packages.x86_64-linux.install-iso = self.inputs.disko.lib.makeDiskImages {
|
||||||
nixosConfig = installer;
|
nixosConfig = installer;
|
||||||
};
|
};
|
||||||
flake.nixosConfigurations = { inherit (clan.nixosConfigurations) installer; };
|
flake.nixosConfigurations = {
|
||||||
|
inherit (clan.nixosConfigurations) installer;
|
||||||
|
};
|
||||||
flake.clanInternals = clan.clanInternals;
|
flake.clanInternals = clan.clanInternals;
|
||||||
flake.apps.x86_64-linux.install-vm.program = installer.config.formats.vm.outPath;
|
flake.apps.x86_64-linux.install-vm.program = installer.config.formats.vm.outPath;
|
||||||
flake.apps.x86_64-linux.install-vm-nogui.program = installer.config.formats.vm-nogui.outPath;
|
flake.apps.x86_64-linux.install-vm-nogui.program = installer.config.formats.vm-nogui.outPath;
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
{ bash
|
{
|
||||||
, callPackage
|
bash,
|
||||||
, coreutils
|
callPackage,
|
||||||
, git
|
coreutils,
|
||||||
, lib
|
git,
|
||||||
, nix
|
lib,
|
||||||
, openssh
|
nix,
|
||||||
, tea
|
openssh,
|
||||||
, tea-create-pr
|
tea,
|
||||||
, ...
|
tea-create-pr,
|
||||||
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
writers = callPackage ../builders/script-writers.nix { };
|
writers = callPackage ../builders/script-writers.nix { };
|
||||||
in
|
in
|
||||||
writers.writePython3Bin "merge-after-ci"
|
writers.writePython3Bin "merge-after-ci" {
|
||||||
{
|
|
||||||
makeWrapperArgs = [
|
makeWrapperArgs = [
|
||||||
"--prefix"
|
"--prefix"
|
||||||
"PATH"
|
"PATH"
|
||||||
@ -28,6 +28,4 @@ writers.writePython3Bin "merge-after-ci"
|
|||||||
tea-create-pr
|
tea-create-pr
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
}
|
} ./merge-after-ci.py
|
||||||
./merge-after-ci.py
|
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{ writeShellApplication
|
{
|
||||||
, bash
|
writeShellApplication,
|
||||||
, curl
|
bash,
|
||||||
|
curl,
|
||||||
}:
|
}:
|
||||||
writeShellApplication {
|
writeShellApplication {
|
||||||
name = "pending-reviews";
|
name = "pending-reviews";
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
{ writeShellApplication
|
{
|
||||||
, bash
|
writeShellApplication,
|
||||||
, coreutils
|
bash,
|
||||||
, git
|
coreutils,
|
||||||
, tea
|
git,
|
||||||
, openssh
|
tea,
|
||||||
|
openssh,
|
||||||
}:
|
}:
|
||||||
writeShellApplication {
|
writeShellApplication {
|
||||||
name = "tea-create-pr";
|
name = "tea-create-pr";
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{ wayland-proxy-virtwl, fetchFromGitHub, libdrm, ocaml-ng }:
|
{
|
||||||
|
wayland-proxy-virtwl,
|
||||||
|
fetchFromGitHub,
|
||||||
|
libdrm,
|
||||||
|
ocaml-ng,
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
ocaml-wayland = ocaml-ng.ocamlPackages_5_0.wayland.overrideAttrs (_old: {
|
ocaml-wayland = ocaml-ng.ocamlPackages_5_0.wayland.overrideAttrs (_old: {
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
@ -16,13 +21,15 @@ wayland-proxy-virtwl.overrideAttrs (_old: {
|
|||||||
rev = "652fca9d4e006a2bdeba920dfaf53190c5373a7d";
|
rev = "652fca9d4e006a2bdeba920dfaf53190c5373a7d";
|
||||||
hash = "sha256-VgpqxjHgueK9eQSX987PF0KvscpzkScOzFkW3haYCOw=";
|
hash = "sha256-VgpqxjHgueK9eQSX987PF0KvscpzkScOzFkW3haYCOw=";
|
||||||
};
|
};
|
||||||
buildInputs = [ libdrm ] ++ (with ocaml-ng.ocamlPackages_5_0; [
|
buildInputs =
|
||||||
ocaml-wayland
|
[ libdrm ]
|
||||||
dune-configurator
|
++ (with ocaml-ng.ocamlPackages_5_0; [
|
||||||
eio_main
|
ocaml-wayland
|
||||||
ppx_cstruct
|
dune-configurator
|
||||||
cmdliner
|
eio_main
|
||||||
logs
|
ppx_cstruct
|
||||||
ppx_cstruct
|
cmdliner
|
||||||
]);
|
logs
|
||||||
|
ppx_cstruct
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
{ stdenv, python3, lib }:
|
{
|
||||||
|
stdenv,
|
||||||
|
python3,
|
||||||
|
lib,
|
||||||
|
}:
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "zerotier-members";
|
name = "zerotier-members";
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{ lib
|
{
|
||||||
, rustPlatform
|
lib,
|
||||||
, fetchFromGitHub
|
rustPlatform,
|
||||||
|
fetchFromGitHub,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
rustPlatform.buildRustPackage {
|
rustPlatform.buildRustPackage {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ self, ... }: {
|
{ self, ... }:
|
||||||
|
{
|
||||||
flake.templates = {
|
flake.templates = {
|
||||||
new-clan = {
|
new-clan = {
|
||||||
description = "Initialize a new clan flake";
|
description = "Initialize a new clan flake";
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core";
|
inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core";
|
||||||
|
|
||||||
outputs = { self, clan-core, ... }:
|
outputs =
|
||||||
|
{ self, clan-core, ... }:
|
||||||
let
|
let
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system};
|
pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system};
|
||||||
@ -17,9 +18,7 @@
|
|||||||
inherit (clan) nixosConfigurations clanInternals;
|
inherit (clan) nixosConfigurations clanInternals;
|
||||||
# add the cLAN cli tool to the dev shell
|
# add the cLAN cli tool to the dev shell
|
||||||
devShells.${system}.default = pkgs.mkShell {
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
packages = [
|
packages = [ clan-core.packages.${system}.clan-cli ];
|
||||||
clan-core.packages.${system}.clan-cli
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user