tests/syncthing: Init test

This commit is contained in:
SebastianStork 2026-03-07 18:02:59 +01:00
parent d2e9850223
commit 5eba758e82
Signed by: SebastianStork
SSH key fingerprint: SHA256:iEM011ogNMG1q8+U500adGu/9rpPuZ2KnFtbdLeqTiI
19 changed files with 254 additions and 17 deletions

View file

@ -10,8 +10,6 @@ let
netCfg = config.custom.networking;
inherit (config.services.syncthing) dataDir;
useSopsSecrets = config.custom.sops.secretsData |> lib.hasAttr "syncthing";
in
{
options.custom.services.syncthing = {
@ -47,31 +45,47 @@ in
"Videos"
];
};
certFile = lib.mkOption {
type = lib.types.nullOr self.lib.types.existingPath;
default = null;
};
keyFile = lib.mkOption {
type = lib.types.nullOr self.lib.types.existingPath;
default = null;
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.isServer -> (cfg.gui.domain != null);
message = "Syncthing requires `gui.domain` to be set when `isServer` is enabled";
}
{
assertion = (cfg.gui.domain != null) -> (self.lib.isPrivateDomain cfg.gui.domain);
message = self.lib.mkUnprotectedMessage "Syncthing-GUI";
}
];
assertions = lib.singleton {
assertion = (cfg.gui.domain != null) -> (self.lib.isPrivateDomain cfg.gui.domain);
message = self.lib.mkUnprotectedMessage "Syncthing-GUI";
};
sops.secrets = lib.mkIf useSopsSecrets {
"syncthing/cert" = {
sops.secrets = {
"syncthing/cert" = lib.mkIf (cfg.certFile == null) {
owner = config.services.syncthing.user;
restartUnits = [ "syncthing.service" ];
};
"syncthing/key" = {
"syncthing/key" = lib.mkIf (cfg.keyFile == null) {
owner = config.services.syncthing.user;
restartUnits = [ "syncthing.service" ];
};
};
environment.etc = {
"syncthing/cert.pem" = lib.mkIf (cfg.certFile != null) {
source = cfg.certFile;
mode = "0644";
user = config.services.syncthing.user;
};
"syncthing/key.pem" = lib.mkIf (cfg.keyFile != null) {
source = cfg.keyFile;
mode = "0600";
user = config.services.syncthing.user;
};
};
services = {
syncthing = {
enable = true;
@ -82,8 +96,16 @@ in
guiAddress = "localhost:${toString cfg.gui.port}";
cert = lib.mkIf useSopsSecrets config.sops.secrets."syncthing/cert".path;
key = lib.mkIf useSopsSecrets config.sops.secrets."syncthing/key".path;
cert =
if (cfg.certFile != null) then
"/etc/syncthing/cert.pem"
else
config.sops.secrets."syncthing/cert".path;
key =
if (cfg.keyFile != null) then
"/etc/syncthing/key.pem"
else
config.sops.secrets."syncthing/key".path;
settings =
let

134
tests/syncthing/default.nix Normal file
View file

@ -0,0 +1,134 @@
{
inputs,
self,
lib,
...
}:
{
node.specialArgs = { inherit inputs self; };
defaults =
{ nodes, config, ... }:
{
imports = [ self.nixosModules.default ];
_module.args.allHosts = nodes |> lib.mapAttrs (_: node: { config = node; });
users = {
mutableUsers = false;
users.seb = {
isNormalUser = true;
password = "seb";
};
};
custom = {
networking.underlay.interface = "eth1";
services = {
nebula = {
caCertificateFile = ./keys/nebula/ca.crt;
certificateFile = ./keys/nebula/${config.networking.hostName}.crt;
privateKeyFile = ./keys/nebula/${config.networking.hostName}.key;
};
syncthing = {
enable = true;
deviceId = ./keys/syncthing/${config.networking.hostName}.id |> lib.readFile |> lib.trim;
certFile = ./keys/syncthing/${config.networking.hostName}.cert;
keyFile = ./keys/syncthing/${config.networking.hostName}.key;
};
};
};
};
nodes = {
server = {
custom = {
networking = {
overlay = {
address = "10.254.250.1";
isLighthouse = true;
role = "server";
};
underlay = {
cidr = "192.168.0.1/16";
isPublic = true;
};
};
services.syncthing.isServer = true;
};
};
client1 = {
custom.networking = {
overlay = {
address = "10.254.250.2";
role = "client";
};
underlay.cidr = "192.168.0.2/16";
};
};
client2 = {
custom = {
networking = {
overlay = {
address = "10.254.250.3";
role = "client";
};
underlay.cidr = "192.168.0.3/16";
};
services.syncthing.folders = [ "Documents" ];
};
};
};
testScript =
{ nodes, ... }:
let
serverNetCfg = nodes.server.custom.networking.overlay;
client1NetCfg = nodes.client1.custom.networking.overlay;
client2NetCfg = nodes.client2.custom.networking.overlay;
getSyncPort = hostName: nodes.${hostName}.custom.services.syncthing.syncPort |> toString;
in
''
start_all()
server.wait_for_unit("syncthing.service")
client1.wait_for_unit("syncthing.service")
client2.wait_for_unit("syncthing.service")
server.wait_for_unit("syncthing-init.service")
client1.wait_for_unit("syncthing-init.service")
client2.wait_for_unit("syncthing-init.service")
server.wait_for_open_port(${getSyncPort "server"}, "${serverNetCfg.address}")
client1.wait_for_open_port(${getSyncPort "client1"}, "${client1NetCfg.address}")
client2.wait_for_open_port(${getSyncPort "client2"}, "${client2NetCfg.address}")
with subtest("Three way sync of Documents"):
server.wait_for_file("/var/lib/syncthing/Documents")
client1.wait_for_file("/home/seb/Documents")
client2.wait_for_file("/home/seb/Documents")
server.succeed("sudo --user=syncthing touch /var/lib/syncthing/Documents/server")
client1.succeed("sudo --user=seb touch /home/seb/Documents/client1")
client2.succeed("sudo --user=seb touch /home/seb/Documents/client2")
server.wait_for_file("/var/lib/syncthing/Documents/client1")
server.wait_for_file("/var/lib/syncthing/Documents/client2")
client1.wait_for_file("/home/seb/Documents/server")
client1.wait_for_file("/home/seb/Documents/client2")
client2.wait_for_file("/home/seb/Documents/server")
client2.wait_for_file("/home/seb/Documents/client1")
with subtest("Two way sync of Pictures"):
server.wait_for_file("/var/lib/syncthing/Pictures")
client1.wait_for_file("/home/seb/Pictures")
client2.fail("test -d /home/seb/Pictures")
'';
}

View file

@ -0,0 +1,5 @@
-----BEGIN NEBULA CERTIFICATE V2-----
MHygFoAEdGVzdIQB/4UEaY8shIYFASWHSoSCIM0af4sq7VnPAySG5h9fwiq/XHvD
a0Ssbk1+KVWFpR71g0DaZP8qR35Zut2z9i9D2bCDuagQNvvxCrkZ3JcF0gMvWu3u
uzKQMKzJSqipppgL/n3iQwwsBAoHYrx1XAY6zXgE
-----END NEBULA CERTIFICATE V2-----

View file

@ -0,0 +1,4 @@
-----BEGIN NEBULA ED25519 PRIVATE KEY-----
8kwpb4GZIphJmamXx0ZrLm5TxPZ7G88L44mrdT2dQp3NGn+LKu1ZzwMkhuYfX8Iq
v1x7w2tErG5NfilVhaUe9Q==
-----END NEBULA ED25519 PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN NEBULA CERTIFICATE V2-----
MIG8oFaAB2NsaWVudDGhBwQFCv76AhijEwwGY2xpZW50DAlzeW5jdGhpbmeFBGmr
V+WGBQElh0qDhyA8ckeBMU2fPOMFe8cEQoAZW3a1/xd+hPuJgkRptJYkIIIg5cgC
6ZPW+oUy91XjQ+2fR37eeSh2Oh0mUeK3QGOf62uDQMur6GhOZatAfhbvYXYpI79Z
4lM7tUWXLH/DBIAMeqVj2iStj5tz8vDNo/ixG2igMYU7ZaDTw7gLgOoue859fQY=
-----END NEBULA CERTIFICATE V2-----

View file

@ -0,0 +1,3 @@
-----BEGIN NEBULA X25519 PRIVATE KEY-----
bpb3z7RewB2Pcg9d+mkMVjoeRRortg+v+f0ljk6jscI=
-----END NEBULA X25519 PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN NEBULA CERTIFICATE V2-----
MIG8oFaAB2NsaWVudDKhBwQFCv76AxijEwwGY2xpZW50DAlzeW5jdGhpbmeFBGmr
V++GBQElh0qDhyA8ckeBMU2fPOMFe8cEQoAZW3a1/xd+hPuJgkRptJYkIIIgVOR5
Nq5iScqDyYvY9f5Hk3jjq7qfxHEUhdAaeCDTx0KDQNoes3D5SbgZNKLtsXpAc8PL
PahMWZoDaySnoLpPJIhRSsav6ZbfUdDGzwbvhW+D8u6sZOqP+HGQSxaPU6ap3As=
-----END NEBULA CERTIFICATE V2-----

View file

@ -0,0 +1,3 @@
-----BEGIN NEBULA X25519 PRIVATE KEY-----
Y4mF5DL8TW/Q+OSGNgh942LqlxuWQA2xntbyWLTQPF0=
-----END NEBULA X25519 PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN NEBULA CERTIFICATE V2-----
MIG7oFWABnNlcnZlcqEHBAUK/voBGKMTDAZzZXJ2ZXIMCXN5bmN0aGluZ4UEaaxC
dIYFASWHSoOHIDxyR4ExTZ884wV7xwRCgBlbdrX/F36E+4mCRGm0liQggiAPf3NY
OWszPbspqt89XZ1KoXShQaLFp+7snVtn4cLnCoNAPtMQGcr0qxFIEEK0K/a5cvk7
Uc7AMLB0zjzUFlw881iI4dGLSSbKjs2c6RMtxmKHOwm9sqCc18QITQ822cwPDg==
-----END NEBULA CERTIFICATE V2-----

View file

@ -0,0 +1,3 @@
-----BEGIN NEBULA X25519 PRIVATE KEY-----
fx4GTdb3a1GUeRoSqFQH3Jb9KEqSipFoYKM/34D16fs=
-----END NEBULA X25519 PRIVATE KEY-----

View file

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBnzCCAVGgAwIBAgIIZ8OMU1XkHLYwBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
CXN5bmN0aGluZzAeFw0yNjAzMDYwMDAwMDBaFw00NjAzMDEwMDAwMDBaMEoxEjAQ
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhADdeAXQaYGTf6XLeVx53
BsAK3ODoIwb/IHobmgTHsSwLo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
c3luY3RoaW5nMAUGAytlcANBADyH0WLqPku8KqsnK5/PyVo6u7YiH961dQmOaDp0
6GyMJogD8ieICsCIt+IeZnhc9CTiJ9++S1RfnF5+R8UVDAs=
-----END CERTIFICATE-----

View file

@ -0,0 +1 @@
IB7WP5L-6BQ2XJE-GQ6LE5A-ADBH4VM-G5KPGN5-JRYKLXP-XOGCVFM-TC7AXAB

View file

@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIIE7hYkrJGXQ6gwZytxBaWvMnqs9kZB01WUrUiWZrXxc
-----END PRIVATE KEY-----

View file

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBnzCCAVGgAwIBAgIIYBqDIQR6+i0wBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
CXN5bmN0aGluZzAeFw0yNjAzMDYwMDAwMDBaFw00NjAzMDEwMDAwMDBaMEoxEjAQ
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhAGI9m1y3be3GDH5n1O9o
FHrGcGQM9Uc6ws7DjzUM/wPho1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
c3luY3RoaW5nMAUGAytlcANBAD39uIYHfausQ7UHoGHp/xsEdYVeybdbfObIPagY
a5udetlOfER5EwGAaqGNSG6C+R+4NgxRU4SU0KMWn6AxDQc=
-----END CERTIFICATE-----

View file

@ -0,0 +1 @@
CZAD2UP-QINQEBX-YO5HKF4-DWXOI2O-C3O7PNP-ATHP2ZE-Y35QSMQ-VM4E3Q6

View file

@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIMGEoiShb3fDL4xKBGbcTD1pm0Q+zMhqcrQDBY7Clvn9
-----END PRIVATE KEY-----

View file

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAVKgAwIBAgIJAPrpCfMh1qXtMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j
dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD
EwlzeW5jdGhpbmcwHhcNMjYwMzA2MDAwMDAwWhcNNDYwMzAxMDAwMDAwWjBKMRIw
EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh
dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQCP17+WnSg6hU2fBqdm
53QMqhPE7uJhofVKPJdxu4xnGaNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CXN5bmN0aGluZzAFBgMrZXADQQBPdsirUqhXmor3UzuFkRMexbiJQQrHCq474DdX
gZyQgfGkxUEtlObNuoD7gVwB0Urn2P5nr9MeJIS//CG5be8F
-----END CERTIFICATE-----

View file

@ -0,0 +1 @@
E74TB3J-GZ2F5ZG-J22G3B6-CJBSPRR-2C43YZJ-5PX47UT-OXJLWPA-EKWEGAB

View file

@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIM0TySbarWhDA+d5N3UecLGiMCR3jsioPIMxCP5H02Bt
-----END PRIVATE KEY-----