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