From d5e090d2d82ccd3b32df4a856284d9ac355dd72c Mon Sep 17 00:00:00 2001 From: Naïm Favier Date: Mon, 22 May 2023 10:28:27 +0200 Subject: Revert "nixos/syncthing: use rfc42 style settings" This reverts commit 32866f8d58979e8dbdf92bfaa72d2883eee861f7. This reverts commit 40a2df0fb0e2c194ded70b7d95a064b1bbf676e7. This reverts commit 4762932601160798aaf20c39ecfc2c202aa6a9e7. --- nixos/modules/services/networking/syncthing.nix | 514 ++++++++++++------------ nixos/tests/syncthing-init.nix | 21 +- 2 files changed, 265 insertions(+), 270 deletions(-) diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 688f46c4492..3d41fe4013e 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -7,26 +7,25 @@ let opt = options.services.syncthing; defaultUser = "syncthing"; defaultGroup = defaultUser; - settingsFormat = pkgs.formats.json { }; - devices = mapAttrsToList (_: device: device // { + devices = mapAttrsToList (name: device: { deviceID = device.id; - }) cfg.settings.devices; - - folders = mapAttrsToList (_: folder: folder // - throwIf (folder?rescanInterval || folder?watch || folder?watchDelay) '' - The options services.syncthing.settings.folders..{rescanInterval,watch,watchDelay} - were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead. - '' { - devices = map (device: - if builtins.isString device then - { deviceId = cfg.settings.devices.${device}.id; } - else - device - ) folder.devices; - }) (filterAttrs (_: folder: + inherit (device) name addresses introducer autoAcceptFolders; + }) cfg.devices; + + folders = mapAttrsToList ( _: folder: { + inherit (folder) path id label type; + devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices; + rescanIntervalS = folder.rescanInterval; + fsWatcherEnabled = folder.watch; + fsWatcherDelayS = folder.watchDelay; + ignorePerms = folder.ignorePerms; + ignoreDelete = folder.ignoreDelete; + versioning = folder.versioning; + }) (filterAttrs ( + _: folder: folder.enable - ) cfg.settings.folders); + ) cfg.folders); updateConfig = pkgs.writers.writeDash "merge-syncthing-config" '' set -efu @@ -55,10 +54,10 @@ let old_cfg=$(curl ${cfg.guiAddress}/rest/config) # generate the new config by merging with the NixOS config options - new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c ${escapeShellArg ''. * ${builtins.toJSON cfg.settings} * { - "devices": (${builtins.toJSON devices}${optionalString (cfg.settings.devices == {} || ! cfg.overrideDevices) " + .devices"}), - "folders": (${builtins.toJSON folders}${optionalString (cfg.settings.folders == {} || ! cfg.overrideFolders) " + .folders"}) - }''}) + new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * { + "devices": (${builtins.toJSON devices}${optionalString (cfg.devices == {} || ! cfg.overrideDevices) " + .devices"}), + "folders": (${builtins.toJSON folders}${optionalString (cfg.folders == {} || ! cfg.overrideFolders) " + .folders"}) + } * ${builtins.toJSON cfg.extraOptions}') # send the new config curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config @@ -100,282 +99,287 @@ in { default = true; description = mdDoc '' Whether to delete the devices which are not configured via the - [devices](#opt-services.syncthing.settings.devices) option. + [devices](#opt-services.syncthing.devices) option. If set to `false`, devices added via the web interface will persist and will have to be deleted manually. ''; }; + devices = mkOption { + default = {}; + description = mdDoc '' + Peers/devices which Syncthing should communicate with. + + Note that you can still add devices manually, but those changes + will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices) + is enabled. + ''; + example = { + bigbox = { + id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; + addresses = [ "tcp://192.168.0.10:51820" ]; + }; + }; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + name = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + The name of the device. + ''; + }; + + addresses = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + The addresses used to connect to the device. + If this is left empty, dynamic configuration is attempted. + ''; + }; + + id = mkOption { + type = types.str; + description = mdDoc '' + The device ID. See . + ''; + }; + + introducer = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether the device should act as an introducer and be allowed + to add folders on this computer. + See . + ''; + }; + + autoAcceptFolders = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Automatically create or share folders that this device advertises at the default path. + See . + ''; + }; + + }; + })); + }; + overrideFolders = mkOption { type = types.bool; default = true; description = mdDoc '' Whether to delete the folders which are not configured via the - [folders](#opt-services.syncthing.settings.folders) option. + [folders](#opt-services.syncthing.folders) option. If set to `false`, folders added via the web interface will persist and will have to be deleted manually. ''; }; - settings = mkOption { - type = types.submodule { - freeformType = settingsFormat.type; - options = { - # global options - options = mkOption { - default = {}; - description = mdDoc '' - The options element contains all other global configuration options - ''; - type = types.submodule ({ name, ... }: { - freeformType = settingsFormat.type; - options = { - localAnnounceEnabled = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - Whether to send announcements to the local LAN, also use such announcements to find other devices. - ''; - }; + folders = mkOption { + default = {}; + description = mdDoc '' + Folders which should be shared by Syncthing. - localAnnouncePort = mkOption { - type = types.int; - default = 21027; - description = lib.mdDoc '' - The port on which to listen and send IPv4 broadcast announcements to. - ''; - }; + Note that you can still add folders manually, but those changes + will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders) + is enabled. + ''; + example = literalExpression '' + { + "/home/user/sync" = { + id = "syncme"; + devices = [ "bigbox" ]; + }; + } + ''; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { - relaysEnabled = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - When true, relays will be connected to and potentially used for device to device connections. - ''; - }; + enable = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to share this folder. + This option is useful when you want to define all folders + in one place, but not every machine should share all folders. + ''; + }; - urAccepted = mkOption { - type = types.int; - default = 0; - description = lib.mdDoc '' - Whether the user has accepted to submit anonymous usage data. - The default, 0, mean the user has not made a choice, and Syncthing will ask at some point in the future. - "-1" means no, a number above zero means that that version of usage reporting has been accepted. - ''; - }; + path = mkOption { + # TODO for release 23.05: allow relative paths again and set + # working directory to cfg.dataDir + type = types.str // { + check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/"); + description = types.str.description + " starting with / or ~/"; + }; + default = name; + description = lib.mdDoc '' + The path to the folder which should be shared. + Only absolute paths (starting with `/`) and paths relative to + the [user](#opt-services.syncthing.user)'s home directory + (starting with `~/`) are allowed. + ''; + }; - limitBandwidthInLan = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc '' - Whether to apply bandwidth limits to devices in the same broadcast domain as the local device. - ''; - }; + id = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + The ID of the folder. Must be the same on all devices. + ''; + }; - maxFolderConcurrency = mkOption { - type = types.int; - default = 0; - description = lib.mdDoc '' - This option controls how many folders may concurrently be in I/O-intensive operations such as syncing or scanning. - The mechanism is described in detail in a [separate chapter](https://docs.syncthing.net/advanced/option-max-concurrency.html). - ''; - }; - }; - }); + label = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + The label of the folder. + ''; }; - # device settings devices = mkOption { - default = {}; + type = types.listOf types.str; + default = []; description = mdDoc '' - Peers/devices which Syncthing should communicate with. - - Note that you can still add devices manually, but those changes - will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices) - is enabled. + The devices this folder should be shared with. Each device must + be defined in the [devices](#opt-services.syncthing.devices) option. ''; - example = { - bigbox = { - id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; - addresses = [ "tcp://192.168.0.10:51820" ]; - }; - }; - type = types.attrsOf (types.submodule ({ name, ... }: { - freeformType = settingsFormat.type; - options = { - - name = mkOption { - type = types.str; - default = name; - description = lib.mdDoc '' - The name of the device. - ''; - }; - - id = mkOption { - type = types.str; - description = mdDoc '' - The device ID. See . - ''; - }; - - autoAcceptFolders = mkOption { - type = types.bool; - default = false; - description = mdDoc '' - Automatically create or share folders that this device advertises at the default path. - See . - ''; - }; - - }; - })); }; - # folder settings - folders = mkOption { - default = {}; + versioning = mkOption { + default = null; description = mdDoc '' - Folders which should be shared by Syncthing. - - Note that you can still add folders manually, but those changes - will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders) - is enabled. + How to keep changed/deleted files with Syncthing. + There are 4 different types of versioning with different parameters. + See . ''; example = literalExpression '' - { - "/home/user/sync" = { - id = "syncme"; - devices = [ "bigbox" ]; - }; - } + [ + { + versioning = { + type = "simple"; + params.keep = "10"; + }; + } + { + versioning = { + type = "trashcan"; + params.cleanoutDays = "1000"; + }; + } + { + versioning = { + type = "staggered"; + fsPath = "/syncthing/backup"; + params = { + cleanInterval = "3600"; + maxAge = "31536000"; + }; + }; + } + { + versioning = { + type = "external"; + params.versionsPath = pkgs.writers.writeBash "backup" ''' + folderpath="$1" + filepath="$2" + rm -rf "$folderpath/$filepath" + '''; + }; + } + ] ''; - type = types.attrsOf (types.submodule ({ name, ... }: { - freeformType = settingsFormat.type; + type = with types; nullOr (submodule { options = { - - enable = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - Whether to share this folder. - This option is useful when you want to define all folders - in one place, but not every machine should share all folders. - ''; - }; - - path = mkOption { - # TODO for release 23.05: allow relative paths again and set - # working directory to cfg.dataDir - type = types.str // { - check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/"); - description = types.str.description + " starting with / or ~/"; - }; - default = name; - description = lib.mdDoc '' - The path to the folder which should be shared. - Only absolute paths (starting with `/`) and paths relative to - the [user](#opt-services.syncthing.user)'s home directory - (starting with `~/`) are allowed. - ''; - }; - - id = mkOption { - type = types.str; - default = name; - description = lib.mdDoc '' - The ID of the folder. Must be the same on all devices. - ''; - }; - - label = mkOption { - type = types.str; - default = name; - description = lib.mdDoc '' - The label of the folder. - ''; - }; - - devices = mkOption { - type = types.listOf types.str; - default = []; + type = mkOption { + type = enum [ "external" "simple" "staggered" "trashcan" ]; description = mdDoc '' - The devices this folder should be shared with. Each device must - be defined in the [devices](#opt-services.syncthing.settings.devices) option. + The type of versioning. + See . ''; }; - - versioning = mkOption { - default = null; + fsPath = mkOption { + default = ""; + type = either str path; description = mdDoc '' - How to keep changed/deleted files with Syncthing. - There are 4 different types of versioning with different parameters. + Path to the versioning folder. See . ''; - example = literalExpression '' - [ - { - versioning = { - type = "simple"; - params.keep = "10"; - }; - } - { - versioning = { - type = "trashcan"; - params.cleanoutDays = "1000"; - }; - } - { - versioning = { - type = "staggered"; - fsPath = "/syncthing/backup"; - params = { - cleanInterval = "3600"; - maxAge = "31536000"; - }; - }; - } - { - versioning = { - type = "external"; - params.versionsPath = pkgs.writers.writeBash "backup" ''' - folderpath="$1" - filepath="$2" - rm -rf "$folderpath/$filepath" - '''; - }; - } - ] - ''; - type = with types; nullOr (submodule { - freeformType = settingsFormat.type; - options = { - type = mkOption { - type = enum [ "external" "simple" "staggered" "trashcan" ]; - description = mdDoc '' - The type of versioning. - See . - ''; - }; - }; - }); }; - - copyOwnershipFromParent = mkOption { - type = types.bool; - default = false; + params = mkOption { + type = attrsOf (either str path); description = mdDoc '' - On Unix systems, tries to copy file/folder ownership from the parent directory (the directory it’s located in). - Requires running Syncthing as a privileged user, or granting it additional capabilities (e.g. CAP_CHOWN on Linux). + The parameters for versioning. Structure depends on + [versioning.type](#opt-services.syncthing.folders._name_.versioning.type). + See . ''; }; }; - })); + }); + }; + + rescanInterval = mkOption { + type = types.int; + default = 3600; + description = lib.mdDoc '' + How often the folder should be rescanned for changes. + ''; + }; + + type = mkOption { + type = types.enum [ "sendreceive" "sendonly" "receiveonly" "receiveencrypted" ]; + default = "sendreceive"; + description = lib.mdDoc '' + Whether to only send changes for this folder, only receive them + or both. `receiveencrypted` can be used for untrusted devices. See + for reference. + ''; + }; + + watch = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether the folder should be watched for changes by inotify. + ''; + }; + + watchDelay = mkOption { + type = types.int; + default = 10; + description = lib.mdDoc '' + The delay after an inotify event is triggered. + ''; + }; + + ignorePerms = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to ignore permission changes. + ''; }; + ignoreDelete = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to skip deleting files that are deleted by peers. + See . + ''; + }; }; - }; + })); + }; + + extraOptions = mkOption { + type = types.addCheck (pkgs.formats.json {}).type isAttrs; default = {}; description = mdDoc '' Extra configuration options for Syncthing. @@ -526,10 +530,6 @@ in { This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher". It can be enabled on a per-folder basis through the web interface. '') - (mkRenamedOptionModule [ "services" "syncthing" "extraOptions" ] [ "services" "syncthing" "settings" ]) - (mkRenamedOptionModule [ "services" "syncthing" "folders" ] [ "services" "syncthing" "settings" "folders" ]) - (mkRenamedOptionModule [ "services" "syncthing" "devices" ] [ "services" "syncthing" "settings" "devices" ]) - (mkRenamedOptionModule [ "services" "syncthing" "options" ] [ "services" "syncthing" "settings" "options" ]) ] ++ map (o: mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ] ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"]; @@ -615,7 +615,9 @@ in { ]; }; }; - syncthing-init = mkIf (cfg.settings != {}) { + syncthing-init = mkIf ( + cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {} + ) { description = "Syncthing configuration updater"; requisite = [ "syncthing.service" ]; after = [ "syncthing.service" ]; diff --git a/nixos/tests/syncthing-init.nix b/nixos/tests/syncthing-init.nix index b26c6103fd4..fcd90739e6a 100644 --- a/nixos/tests/syncthing-init.nix +++ b/nixos/tests/syncthing-init.nix @@ -9,21 +9,14 @@ in { nodes.machine = { services.syncthing = { enable = true; - settings = { - options.crashReportingEnabled = false; - devices.testDevice = { - id = testId; - }; - folders.testFolder = { - path = "/tmp/test"; - devices = [ "testDevice" ]; - versioning = { - type = "simple"; - params.keep = "10"; - }; - }; - gui.user = "guiUser"; + devices.testDevice = { + id = testId; }; + folders.testFolder = { + path = "/tmp/test"; + devices = [ "testDevice" ]; + }; + extraOptions.gui.user = "guiUser"; }; }; -- cgit 1.4.1