at master 7.8 kB view raw
1{ lib, pkgs, ... }: 2 3# This nixosTest is supposed to check the following: 4# 5# - Whether syncthing's API handles multiple requests for many devices, see 6# https://github.com/NixOS/nixpkgs/issues/260262 7# 8# - Whether syncthing-init.service generated bash script removes devices and 9# folders that are not present in the user's configuration, which is partly 10# injected into the script. See also: 11# https://github.com/NixOS/nixpkgs/issues/259256 12# 13 14let 15 # Just a long path not to copy paste 16 configPath = "/var/lib/syncthing/.config/syncthing/config.xml"; 17 18 # We will iterate this and more attribute sets defined here, later in the 19 # testScript. Start with this, and distinguish these settings from other 20 # settings, as we check these differently with xmllint, due to the ID. 21 settingsWithId = { 22 devices = { 23 # All of the device IDs used here were generated by the following command: 24 # 25 # (${pkgs.syncthing}/bin/syncthing generate --home /tmp/foo\ 26 # | grep ID: | sed 's/.*ID: *//') && rm -rf /tmp/foo 27 # 28 # See also discussion at: 29 # https://forum.syncthing.net/t/how-to-generate-dummy-device-ids/20927/8 30 test_device1.id = "IVTZ5XF-EF3GKFT-GS4AZLG-IT6H2ZP-6WK75SF-AFXQXJJ-BNRZ4N6-XPDKVAU"; 31 test_device2.id = "5C35H56-Z2GFF4F-F3IVD4B-GJYVWIE-SMDBJZN-GI66KWP-52JIQGN-4AVLYAM"; 32 test_device3.id = "XKLSKHE-BZOHV7B-WQZACEF-GTH36NP-6JSBB6L-RXS3M7C-EEVWO2L-C5B4OAJ"; 33 test_device4.id = "APN5Q7J-35GZETO-5KCLF35-ZA7KBWK-HGWPBNG-FERF24R-UTLGMEX-4VJ6PQX"; 34 test_device5.id = "D4YXQEE-5MK6LIK-BRU5QWM-ZRXJCK2-N3RQBJE-23JKTQQ-LYGDPHF-RFPZIQX"; 35 test_device6.id = "TKMCH64-T44VSLI-6FN2YLF-URBZOBR-ATO4DYX-GEDRIII-CSMRQAI-UAQMDQG"; 36 test_device7.id = "472EEBG-Q4PZCD4-4CX6PGF-XS3FSQ2-UFXBZVB-PGNXWLX-7FKBLER-NJ3EMAR"; 37 test_device8.id = "HW6KUMK-WTBG24L-2HZQXLO-TGJSG2M-2JG3FHX-5OGYRUJ-T6L5NN7-L364QAZ"; 38 test_device9.id = "YAE24AP-7LSVY4T-J74ZSEM-A2IK6RB-FGA35TP-AG4CSLU-ED4UYYY-2J2TDQU"; 39 test_device10.id = "277XFSB-OFMQOBI-3XGNGUE-Y7FWRV3-QQDADIY-QIIPQ26-EOGTYKW-JP2EXAI"; 40 test_device11.id = "2WWXVTN-Q3QWAAY-XFORMRM-2FDI5XZ-OGN33BD-XOLL42R-DHLT2ML-QYXDQAU"; 41 }; 42 # Generates a few folders with IDs and paths as written... 43 folders = lib.pipe 6 [ 44 (builtins.genList (x: { 45 name = "/var/lib/syncthing/test_folder${builtins.toString x}"; 46 value = { 47 id = "DontDeleteMe${builtins.toString x}"; 48 }; 49 })) 50 builtins.listToAttrs 51 ]; 52 }; 53 # Non default options that we check later if were applied 54 settingsWithoutId = { 55 options = { 56 autoUpgradeIntervalH = 0; 57 urAccepted = -1; 58 }; 59 gui = { 60 theme = "dark"; 61 }; 62 }; 63 # Used later when checking whether settings were set in config.xml: 64 checkSettingWithId = 65 { 66 t, # t for type 67 id, 68 not ? false, 69 }: 70 '' 71 print("Searching for a ${t} with id ${id}") 72 configVal_${t} = machine.succeed( 73 "${pkgs.libxml2}/bin/xmllint " 74 "--xpath 'string(//${t}[@id=\"${id}\"]/@id)' ${configPath}" 75 ) 76 print("${t}.id = {}".format(configVal_${t})) 77 assert "${id}" ${if not then "not" else ""} in configVal_${t} 78 ''; 79 # Same as checkSettingWithId, but for 'options' and 'gui' 80 checkSettingWithoutId = 81 { 82 t, # t for type 83 n, # n for name 84 v, # v for value 85 not ? false, 86 }: 87 '' 88 print("checking whether setting ${t}.${n} is set to ${v}") 89 configVal_${t}_${n} = machine.succeed( 90 "${pkgs.libxml2}/bin/xmllint " 91 "--xpath 'string(/configuration/${t}/${n})' ${configPath}" 92 ) 93 print("${t}.${n} = {}".format(configVal_${t}_${n})) 94 assert "${v}" ${if not then "not" else ""} in configVal_${t}_${n} 95 ''; 96 # Removes duplication a bit to define this function for the IDs to delete - 97 # we check whether they were added after our script ran, and before the 98 # systemd unit's bash script ran, and afterwards - whether the systemd unit 99 # worked. 100 checkSettingsToDelete = 101 { 102 not, 103 }: 104 lib.pipe IDsToDelete [ 105 (lib.mapAttrsToList ( 106 t: id: 107 checkSettingWithId { 108 inherit t id; 109 inherit not; 110 } 111 )) 112 lib.concatStrings 113 ]; 114 # These IDs are added to syncthing using the API, similarly to how the 115 # generated systemd unit's bash script does it. Only we add it and expect the 116 # systemd unit bash script to remove them when executed. 117 IDsToDelete = { 118 # Also created using the syncthing generate command above 119 device = "LZ2CTHT-3W2M7BC-CMKDFZL-DLUQJFS-WJR73PA-NZGODWG-DZBHCHI-OXTQXAK"; 120 # Intentionally this is a substring of the IDs of the 'test_folder's, as 121 # explained in: https://github.com/NixOS/nixpkgs/issues/259256 122 folder = "DeleteMe"; 123 }; 124 addDeviceToDeleteScript = pkgs.writers.writeBash "syncthing-add-device-to-delete.sh" '' 125 set -euo pipefail 126 127 export RUNTIME_DIRECTORY=/tmp 128 129 curl() { 130 # get the api key by parsing the config.xml 131 while 132 ! ${pkgs.libxml2}/bin/xmllint \ 133 --xpath 'string(configuration/gui/apikey)' \ 134 ${configPath} \ 135 >"$RUNTIME_DIRECTORY/api_key" 136 do sleep 1; done 137 138 (printf "X-API-Key: "; cat "$RUNTIME_DIRECTORY/api_key") >"$RUNTIME_DIRECTORY/headers" 139 140 ${pkgs.curl}/bin/curl -sSLk -H "@$RUNTIME_DIRECTORY/headers" \ 141 --retry 1000 --retry-delay 1 --retry-all-errors \ 142 "$@" 143 } 144 curl -d ${lib.escapeShellArg (builtins.toJSON { deviceID = IDsToDelete.device; })} \ 145 -X POST 127.0.0.1:8384/rest/config/devices 146 curl -d ${lib.escapeShellArg (builtins.toJSON { id = IDsToDelete.folder; })} \ 147 -X POST 127.0.0.1:8384/rest/config/folders 148 ''; 149in 150{ 151 name = "syncthing-many-devices"; 152 meta.maintainers = with lib.maintainers; [ doronbehar ]; 153 154 nodes.machine = { 155 services.syncthing = { 156 enable = true; 157 overrideDevices = true; 158 overrideFolders = true; 159 settings = settingsWithoutId // settingsWithId; 160 }; 161 }; 162 testScript = '' 163 machine.wait_for_unit("syncthing-init.service") 164 '' 165 + (lib.pipe settingsWithId [ 166 # Check that folders and devices were added properly and that all IDs exist 167 (lib.mapAttrsRecursive ( 168 path: id: 169 checkSettingWithId { 170 # plural -> solitary 171 t = (lib.removeSuffix "s" (builtins.elemAt path 0)); 172 inherit id; 173 } 174 )) 175 # Get all the values we applied the above function upon 176 (lib.collect builtins.isString) 177 lib.concatStrings 178 ]) 179 + (lib.pipe settingsWithoutId [ 180 # Check that all other syncthing.settings were added properly with correct 181 # values 182 (lib.mapAttrsRecursive ( 183 path: value: 184 checkSettingWithoutId { 185 t = (builtins.elemAt path 0); 186 n = (builtins.elemAt path 1); 187 v = (builtins.toString value); 188 } 189 )) 190 # Get all the values we applied the above function upon 191 (lib.collect builtins.isString) 192 lib.concatStrings 193 ]) 194 + '' 195 # Run the script on the machine 196 machine.succeed("${addDeviceToDeleteScript}") 197 '' 198 + (checkSettingsToDelete { 199 not = false; 200 }) 201 + '' 202 # Useful for debugging later 203 machine.copy_from_vm("${configPath}", "before") 204 205 machine.systemctl("restart syncthing-init.service") 206 machine.wait_for_unit("syncthing-init.service") 207 '' 208 + (checkSettingsToDelete { 209 not = true; 210 }) 211 + '' 212 # Useful for debugging later 213 machine.copy_from_vm("${configPath}", "after") 214 215 # Copy the systemd unit's bash script, to inspect it for debugging. 216 mergeScript = machine.succeed( 217 "systemctl cat syncthing-init.service | " 218 "${pkgs.initool}/bin/initool g - Service ExecStart --value-only" 219 ).strip() # strip from new lines 220 machine.copy_from_vm(mergeScript, "") 221 ''; 222}