nixosTests.syncthing-folders: avoid IFD (#505674)

This commit is contained in:
Diogo Correia
2026-09-28 10:19:21 +00:00
committed by GitHub
11 changed files with 234 additions and 140 deletions

View File

@@ -1,148 +1,197 @@
{ lib, pkgs, ... }:
let
genNodeId =
name:
pkgs.runCommand "syncthing-test-certs-${name}" { } ''
mkdir -p $out
${pkgs.syncthing}/bin/syncthing generate --home=$out
${pkgs.libxml2}/bin/xmllint --xpath 'string(configuration/device/@id)' $out/config.xml > $out/id
'';
idA = genNodeId "a";
idB = genNodeId "b";
idC = genNodeId "c";
testPassword = "it's a secret";
in
{
name = "syncthing";
meta.maintainers = with pkgs.lib.maintainers; [ zarelit ];
nodeNames = [
"a"
"b"
"c"
];
nodeDirs = lib.genAttrs nodeNames (n: ./test-nodes + "/${n}");
nodeData = lib.mapAttrs (n: v: {
cert = "${v}/cert.pem";
key = "${v}/key.pem";
id = lib.fileContents (v + "/id");
}) nodeDirs;
nodes = {
a =
{ config, ... }:
{
environment.etc.bar-encryption-password.text = testPassword;
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = "${idA}/cert.pem";
key = "${idA}/key.pem";
guiAddress = "unix:///run/syncthing/syncthing.sock";
settings = {
devices.b.id = lib.fileContents "${idB}/id";
devices.c.id = lib.fileContents "${idC}/id";
folders.foo = {
path = "/var/lib/syncthing/foo";
devices = [ "b" ];
};
folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
{
name = "c";
encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
}
];
};
folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"b"
"c"
];
ignorePatterns = [ ];
};
folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [
"b"
];
};
};
};
};
b =
{ config, ... }:
{
environment.etc.bar-encryption-password.text = testPassword;
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = "${idB}/cert.pem";
key = "${idB}/key.pem";
settings = {
devices.a.id = lib.fileContents "${idA}/id";
devices.c.id = lib.fileContents "${idC}/id";
folders.foo = {
path = "/var/lib/syncthing/foo";
devices = [ "a" ];
};
folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
{
name = "c";
encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
}
];
};
folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"c"
];
ignorePatterns = [
"notB"
];
};
# Test how we handle white spaces in folder IDs
folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [
"a"
];
ignorePatterns = [
"notB"
# Just test that an apostrophe doesn't break the curl config
# commands. See: https://github.com/NixOS/nixpkgs/issues/554744
"apostrophe'"
];
};
};
};
};
c = {
services.syncthing = {
enable = true;
openDefaultPorts = true;
cert = "${idC}/cert.pem";
key = "${idC}/key.pem";
settings = {
devices.a.id = lib.fileContents "${idA}/id";
devices.b.id = lib.fileContents "${idB}/id";
folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
"a"
"b"
];
type = "receiveencrypted";
};
folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"b"
];
ignorePatterns = [
"notC"
];
};
};
};
testPassword = "it's a secret";
commonNodeConfigModule = {
services.syncthing = {
enable = true;
openDefaultPorts = true;
settings.devices = lib.mapAttrs (n: v: { inherit (v) id; }) nodeData;
};
};
nodeConfigModules = {
a = {
services.syncthing = {
inherit (nodeData.a) cert key;
guiAddress = "unix:///run/syncthing/syncthing.sock";
};
};
b = {
services.syncthing = { inherit (nodeData.b) cert key; };
};
c = {
services.syncthing = { inherit (nodeData.c) cert key; };
};
};
nodeFolderConfigModules = [
# "foo" is a folder that is synchronised only between nodes a and b.
rec {
a = {
services.syncthing.settings.folders.foo = {
path = "/var/lib/syncthing/foo";
devices = [
"a"
"b"
];
};
};
b = a;
c = { };
}
# "bar" is synchronised between a and c, and between b and c, but c only
# gets an encrypted copy, and a and b never synchronise directly to each
# other.
rec {
a =
{ config, ... }:
{
environment.etc.bar-encryption-password.text = testPassword;
services.syncthing.settings.folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
{
name = "c";
encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
}
];
};
};
b = a;
c = {
services.syncthing.settings.folders.bar = {
path = "/var/lib/syncthing/bar";
devices = [
"a"
"b"
];
type = "receiveencrypted";
};
};
}
# "baz" is synchronised between all three nodes, but has filters on b and c
# that mean they shouldn't receive certain files.
{
a = {
services.syncthing.settings.folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"b"
"c"
];
ignorePatterns = [ ];
};
};
b = {
services.syncthing.settings.folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"c"
];
ignorePatterns = [
"notB"
# Just test that an apostrophe doesn't break the curl config
# commands. See: https://github.com/NixOS/nixpkgs/issues/554744
"apostrophe'"
];
};
};
c = {
services.syncthing.settings.folders.baz = {
path = "/var/lib/syncthing/baz";
devices = [
"a"
"b"
];
ignorePatterns = [ "notC" ];
};
};
}
# "foo bar" tests handling whitespace in folder IDs.
{
a = {
services.syncthing.settings.folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [ "b" ];
};
};
b = {
services.syncthing.settings.folders."foo bar" = {
path = "/var/lib/syncthing/foo-bar";
devices = [ "a" ];
ignorePatterns = [ "notB" ];
};
};
c = { };
}
];
in
{
name = "syncthing-folders";
meta.maintainers = with pkgs.lib.maintainers; [ zarelit ];
# Run from the root of the nixpkgs repository with
#
# nix-build -A nixosTests.syncthing-folders.genNodeData &&
# ./result/bin/genNodeData.sh
#
# This generates new keys, certificates, and overall Syncthing config, and
# updates the certificate and key files and the ID file extracted from the
# overall Syncthing config file.
passthru.genNodeData = pkgs.writeShellApplication {
name = "genNodeData.sh";
runtimeInputs = with pkgs; [
syncthing
libxml2
];
text = ''
rm -r nixos/tests/syncthing/test-nodes
mkdir nixos/tests/syncthing/test-nodes
cd nixos/tests/syncthing/test-nodes
for d in ${lib.escapeShellArgs nodeNames}; do
mkdir -- "$d"
syncthing generate --home="$d"
xmllint --xpath 'string(configuration/device/@id)' "$d"/config.xml >"$d"/id
rm -f -- "$d"/.syncthing.tmp.* "$d"/config.xml
done
'';
};
nodes = lib.genAttrs nodeNames (n: {
imports = [
commonNodeConfigModule
nodeConfigModules."${n}"
]
++ map (builtins.getAttr n) nodeFolderConfigModules;
});
testScript = ''
start_all()

View File

@@ -1,6 +1,6 @@
{ lib, pkgs, ... }:
{
name = "syncthing";
name = "syncthing-no-settings";
meta.maintainers = with pkgs.lib.maintainers; [ chkno ];
nodes = {

View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAVKgAwIBAgIJAJ7l/z0JF6aOMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j
dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD
EwlzeW5jdGhpbmcwHhcNMjYwNDAyMDAwMDAwWhcNNDYwMzI4MDAwMDAwWjBKMRIw
EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh
dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQBCsJ1O6QrxxFP/YKKj
WAjdZp07AiTIIC0/p/mHGbmyraNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CXN5bmN0aGluZzAFBgMrZXADQQBqGFXpwvEoAc7N6mT9evXI3++STiTE6Lu3Z2z3
ecp5vU3fS5U+b0fO4BEUrNoaDdXurfOal6+LoydAnJcFamwN
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
F2ACIUG-FML5RHY-ATV55ZN-5KGVUIV-RWFG3Y6-QPLTKZB-NWUZAGD-7QRCWAP

View File

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

View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBnzCCAVGgAwIBAgIIKyLKAcqLWn4wBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
CXN5bmN0aGluZzAeFw0yNjA0MDIwMDAwMDBaFw00NjAzMjgwMDAwMDBaMEoxEjAQ
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhABoahydRmwpbCII6mz9i
E8FeGH7YdHqgMeAmLiFZu2wMo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
c3luY3RoaW5nMAUGAytlcANBADFVKB2vF16j0gc/h71x3vUi042FbmXTPk9kMb2O
9O0NnWSoMuOGPFDEoHWBFuUuixQ/3cw45zw+fea28m95gQo=
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
LOB4D2H-OYG64QZ-NDX43LJ-NYR4HQU-BRNXCNI-ERACWFP-OVURLBI-63MR5QP

View File

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

View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAVKgAwIBAgIJAIZk5+sv3EbTMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j
dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD
EwlzeW5jdGhpbmcwHhcNMjYwNDAyMDAwMDAwWhcNNDYwMzI4MDAwMDAwWjBKMRIw
EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh
dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQB0QK+PM7oLutgCKoIo
UOh8/XiSmGJWbkN175hALTIaYKNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CXN5bmN0aGluZzAFBgMrZXADQQAbedm895vhgYizMXc38IwhquYv2S4ORHZac0Bw
ZYKJKze7EhKzqvdxcU5uVIUaMPSGi86wtmmsiijfhY8rnD0E
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
MPGAF2L-AUIF5DE-UCI3AMX-PTGF3ZI-XDS6UJI-YUU4ZWT-UUWY7X6-N2DAAQG

View File

@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIMSmcIlXQTKkaMaOLh+zsgU1ULCvCz949E56OzQ1dsMK
-----END PRIVATE KEY-----